RSA 기반 웹페이지 암호화 로그인 프로그래밍

PS>아래 내용은 보안상 검증된 것이 아닙니다. 잘 확인하고 사용하세요.

웹 페이지에서 SSL 없이 RSA 암호화 로그인 하기

사용자의 비밀번호를 전송할 때는 SSL 등의 처리를 하지 않으면 해당 비밀번호를 중간에 가로채서 보는 것이 가능하다. 그러나 비영리 싸이트 혹은 SSL 인증서 구매가 어려운 경우에 JavaScript로 RSA 암호화를 이용해서 암호화된 로그인이 가능하다.

RSA는 비대칭 방식으로 암호화는 공개키(누구나 볼 수 있다)로 하고 복호화는 개인키를 가진쪽만 가능한 형태이다.

사용자가 로그인 폼을 채우면 사용자 ID와 비밀번호를 RSA 공개키로 암호화해서 전송하여, 중간에 패킷을 가로채도 해석이 불가능하게 만드는 것이다.

 

이와 같은 것을 구현하고 싶어진 계기는 회사 내부적으로 사용하는 운영용 싸이트의 로그인 정보를 암호화해야 겠다는 생각이 들었기 때문이다. 그러면서  돈 안 쓰고 특정 브라우저에 종속되지 않으면서도 안전한 사이트 이용을 가능하게 하는 방법을 찾다가 메가박스 홈페이지가 2010년 6월 현재 SSL과 ActiveX없이 그렇게 구현돼 있는 것을 보았다.

이와 같은 로그인 및 데이터 전송 방식에 대해서는 알아야 막는다 자바 JSP 해킹과 보안 책에서 정보를 얻어서 BigIntegers and RSA in JavaScript 라이브러리를 사용하였다.

참고로 해당 라이브러리에서 제공하는 BASE64 인코더에 문제가 있는 것으로 보인다. 책에서 소개한대로 이 BASE64 인코딩 라이브러리를 사용하면 FireFox에서는 오동작을 한다. 그래서 BASE64를 사용하지 않고 암호화된 바이트 배열을 그냥 16진 문자열(hex)로 서버에 전송한다.

알아야 막는다 자바 JSP 해킹과 보안 책의 방식대로 하면 세션에 무한정 PrivateKey를 저장해서 메모리 누수를 일으킬 수 있다. 이 책 그대로 따라하면 안된다.

 

사실 그냥 HTTPS 사용하면 더 안전하게 아무 처리 없이 다 해결되는 문제들이다. 하지만 내부 적으로만 사용하는 서비스들에 대해서 일일이 다 인증서를 등록할 수는 없으니 지금 소개하는 방식이 가장 쉽고 돈 안들이면서 안전하게 로그인 할 수 있는 방법이 되어 줄 것이다.

 

기본 작동 원리

  1. [서버] 서버측에서 RSA 공개키와 개인키(비밀키)를 생성하여, 개인키는 세션에 저장하고 공개키는 자바스크립트 로그인 폼이 있는 페이지로 출력한다.
  2. [클라이언트] 로그인폼은 자바스크립트가 실행되지 않는 환경에서는 발행(submit)이 되면 안된다.
  3. [클라이언트] 로그인폼에 사용자의 ID와 비밀번호를 넣고 발행을 누르면 자바스크립트가 이를 가로챈다.

    1. 사용자 ID를 RSA로 암호화하여 화면에 안보이는 새로운 폼에 저장한다.
    2. 비밀번호를 RSA로 암호화하여 화면에 안보이는 새로운 폼에 저장한다.
    3. 이제 화면에 안보이는 해당 폼을 서버로 발행한다.
  4. [서버] 서버측에서 세션에 저장된 RSA개인키로 암호화된 사용자ID와 비밀번호를 복호화한다.
  5. [서버] 데이터베이스/혹은 기타 저장소에 저장된 사용자 ID와 비밀번호가 일치하는지 확인한다.

 

주의할 점. 암호화 된 값은 byte 배열이다. 이를 문자열 폼으로 전송하기 위해 16진 문자열(hex)로 변경한다. 서버측에서도 값을 받을 때 hex 문자열을 받아서 이를 다시 byte 배열로 바꾼 뒤에 복호화 과정을 수행한다.

 

Java 로그인 폼 측 키생성

로그인 폼을 보여주는 화면을 출력할 때, 그와 동시에 공개키와 개인키를 생성해서 공개키는 HTML/Javascript에서 접근 가능하게 노출하고 개인키는 HTTP 세션에 보관하여 암호를 풀 때 사용하도록 처리한다.

  1. KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    generator.initialize(KEY_SIZE);

    KeyPair keyPair = generator.genKeyPair();
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");

    PublicKey publicKey = keyPair.getPublic();
    PrivateKey privateKey = keyPair.getPrivate();


    HttpSession session = request.getSession();
    // 세션에 공개키의 문자열을 키로하여 개인키를 저장한다.
    session.setAttribute("__rsaPrivateKey__", privateKey);

    // 공개키를 문자열로 변환하여 JavaScript RSA 라이브러리 넘겨준다.
    RSAPublicKeySpec publicSpec = (RSAPublicKeySpec) keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);

    String publicKeyModulus = publicSpec.getModulus().toString(16);
    String publicKeyExponent = publicSpec.getPublicExponent().toString(16);

    request.setAttribute("publicKeyModulus", publicKeyModulus);
    request.setAttribute("publicKeyExponent", publicKeyExponent);

    request.getRequestDispatcher("/WEB-INF/views/loginForm.jsp").forward(request, response);

 

loginForm.jsp 에서 실제로 폼을 출력하는 역할을 한다.

 

HTML 폼

  1.         <!-- script 태그에서 가져오는 자바스크립트 파일의 순서에 주의해야한다! 순서가 틀릴경우 자바스크립트 오류가 발생한다. -->
            <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/jsbn.js"></script>
            <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/rsa.js"></script>
            <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/prng4.js"></script>
            <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/rng.js"></script>

            <script type="text/javascript" src="<%=request.getContextPath()%>/js/login.js"></script>
        </head>
        <body>
            <div>
                <label for="username">사용자ID : <input type="text" id="username" size="16"/></label>
                <label for="password">비밀번호 : <input type="password" id="password" size="16" /></label>
                <input type="hidden" id="rsaPublicKeyModulus" value="<%=publicKeyModulus%>" />
                <input type="hidden" id="rsaPublicKeyExponent" value="<%=publicKeyExponent%>" />

                <a href="<%=request.getContextPath()%>/loginFailure.jsp" onclick="validateEncryptedForm(); return false;">로그인</a>
            </div>
            <form id="securedLoginForm" name="securedLoginForm" action="<%=request.getContextPath()%>/login" method="post" style="display: none;">
                <input type="hidden" name="securedUsername" id="securedUsername" value="" />
                <input type="hidden" name="securedPassword" id="securedPassword" value="" />
            </form>

        </body>

 

폼을 이중으로 만들었는데, 이유가 있다. 폼에 submit 버튼을 두게되면 사용자가 그냥 엔터를 쳐도 폼이 제출되게 된다. 이렇게 되면 사용자의 웹 브라우저가 Javascript를 지원하지 않아도 폼이 제출되므로 사용자가 쓴 아이디와 비밀번호가 그냥 전송되게 돼 버린다.

사용자가 ID와 비밀번호를 친 뒤에 무조건 Javascript를 타게 만들기 위해 입력용 폼과 Javascript로 암호화하여 실제로 제출하는 폼을 분리한 것이다.

만약 사용자의 브라우저가 javascript를 지원하지 않는다면, 로그인 링크의 loginFailure.jsp 페이지가 보여지게 된다.

 

자바스크립트 암호화 처리

  1. function validateEncryptedForm() {
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;
        if (!username || !password) {
            alert("ID/비밀번호를 입력해주세요.");
            return false;
        }

        try {
            var rsaPublicKeyModulus = document.getElementById("rsaPublicKeyModulus").value;
            var rsaPublicKeyExponent = document.getElementById("rsaPublicKeyExponent").value;
            submitEncryptedForm(username,password, rsaPublicKeyModulus, rsaPublicKeyExponent);
        } catch(err) {
            alert(err);
        }
        return false;
    }

    function submitEncryptedForm(username, password, rsaPublicKeyModulus, rsaPpublicKeyExponent) {
        var rsa = new RSAKey();
        rsa.setPublic(rsaPublicKeyModulus, rsaPpublicKeyExponent);

        // 사용자ID와 비밀번호를 RSA로 암호화한다.
        var securedUsername = rsa.encrypt(username);
        var securedPassword = rsa.encrypt(password);

        // POST 로그인 폼에 값을 설정하고 발행(submit) 한다.
        var securedLoginForm = document.getElementById("securedLoginForm");
        securedLoginForm.securedUsername.value = securedUsername;
        securedLoginForm.securedPassword.value = securedPassword;
        securedLoginForm.submit();

 

 

실질적인 암호화는 submitEncryptedForm에서 이뤄진다. 공개키의 rsaPublicKeyModulus와 rsaPublicKeyExponent 값을 읽어 RSA 객체를 구성하고 이를 가지고 사용자의 ID와 비밀번호를 모두 암호화하여 전송용 폼에 암호화된 값을 지정하고 폼을 제출(submit)한다.

 

Java 측 복호화하여 사용자 ID,비밀번호 확인

  1.     /**
         * 암호화된 비밀번호를 복호화 한다.
         */
        protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String securedUsername = request.getParameter("securedUsername");
            String securedPassword = request.getParameter("securedPassword");

            HttpSession session = request.getSession();
            PrivateKey privateKey = (PrivateKey) session.getAttribute("__rsaPrivateKey__");
            session.removeAttribute("__rsaPrivateKey__"); // 키의 재사용을 막는다. 항상 새로운 키를 받도록 강제.


            if (privateKey == null) {
                throw new RuntimeException("암호화 비밀키 정보를 찾을 수 없습니다.");
            }
            try {
                String username = decryptRsa(privateKey, securedUsername);
                String password = decryptRsa(privateKey, securedPassword);
                request.setAttribute("username", username);
                request.setAttribute("password", password);
                request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
            } catch (Exception ex) {
                throw new ServletException(ex.getMessage(), ex);
            }
        }

        private String decryptRsa(PrivateKey privateKey, String securedValue) throws Exception {
            System.out.println("will decrypt : " + securedValue);
            Cipher cipher = Cipher.getInstance("RSA");
            byte[] encryptedBytes = hexToByteArray(securedValue);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            String decryptedValue = new String(decryptedBytes, "utf-8"); // 문자 인코딩 주의.
            return decryptedValue;
        }


        /**
         * 16진 문자열을 byte 배열로 변환한다.
         */
        public static byte[] hexToByteArray(String hex) {
            if (hex == null || hex.length() % 2 != 0) {
                return new byte[]{};
            }

            byte[] bytes = new byte[hex.length() / 2];
            for (int i = 0; i < hex.length(); i += 2) {
                byte value = (byte)Integer.parseInt(hex.substring(i, i + 2), 16);
                bytes[(int) Math.floor(i / 2)] = value;
            }
            return bytes;
        }

 

사용자의 로그인 정보를 받아서 실제 사용자가 맞는지 확인하는쪽 컨트롤러이다. 여기서는 그냥 사용자의 입력을 화면에 다시 출력해주도록 만들었다.

HTTP 세션에서 앞서 저장한 개인키를 읽는다. 만약 변조된 공개키로 암호화를 했거나 개인키가 존재하지 않는 상황이라면 물론 오류가 발생한다.

그리고 세션에서 개인키를 지워버린다. 절대로 동일 개인키로 두 번이상 로그인 할 수 없게 만들었다.

 

어떻게 전송되나

아래 이미지는 실제 전송되는 데이터를 Charles 라는 HTTP 프록시 툴로 살펴본 것이다. 사용자의 ID와 비밀번호가 모두 암호화되어 원형을 알아 볼 수 없음을 볼 수 있다.

 

 

이상이 끝. 관련 파일을 모두 함께 올려둔다. WAR 는 실행하고서 http://localhost:8080/RSATest 로 접속하면 된다.


RSATest.war

 

기타

사실 정말로 원한 것은 AES로 사용자의 정보를 암호화하고 AES키를 RSA공개키로 암호화하여 서버에 전달 한뒤, 서버에서 RSA 개인키로 AES키를 복호화해 알아내고, 그 AES키로 다시 사용자 정보를 복호화하는 이중 암호화를 시도했었다.

그러나 그닥 녹록치 않아서, 일단 RSA 기반의 단일 암호화로 만족한 상태이다.

 

이 글은 스프링노트에서 작성되었습니다.


핑백

덧글

  • 토비 2010/07/07 16:30 # 삭제

    오.. 멋있어요.
  • 권남 2010/07/07 21:20 #

    감사합니다. ^^
  • 수평선 2010/07/13 00:38 # 삭제

    감사합니다.
    별거아니지만 직접 테스트 해보니 오타가 있네요..
    LoginServlet.java 파일에서
    PrivateKey privateKey = (PrivateKey) session.getAttribute("rsaPrivateKey");

    PrivateKey privateKey = (PrivateKey) session.getAttribute("__rsaPrivateKey__");
    로 바꿔야 할것 같습니다.
  • 권남 2010/07/13 01:33 #

    앗, 네 감사합니다. 코드를 붙여넣는 타이밍이 달랐었네요. 문서 작성하면서 코드 수정하면서 그랬거든요. ㅎㅎ
  • 그러니까 2011/06/17 16:28 # 삭제

    제가 잘 몰라서 그런지 처음에 에러가 나더라고요.
    java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPrivateCrtKeyImpl
    at javax.crypto.Cipher.a(DashoA13*..)

    bcprov-jdk16-146.jar 추가 후
    소스를 아래와 같이 바꾸니 잘되네요
    감사합니다. ^^
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

    //Cipher cipher = Cipher.getInstance("RSA");
    Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
  • 권남 2011/06/18 15:49 #

    아~ 좋은 정보 감사합니다!
  • 굿잡~ 2011/09/06 16:53 # 삭제

    훌륭합니다.!!
    감사합니다~~~~~~
  • 권남 2012/01/14 00:15 #

  • 782011 2012/03/15 23:47 # 삭제

    재미있는 정보네요...
  • mountie 2012/05/03 10:51 # 삭제

    이방식의 취약점은 HTTP Session을 이용하는것입니다.
    Session hijacking, XSS, CSRF attack등의 공격에 취약하므로
    HTTP Session하에서 이용하면 매우 위험합니다.

    가급적 HTTPS Session에서 session 보안 관련 설정을 해두고 (cookie secure, httponly 설정 등)
    이용하는것을 권장합니다.
  • mountie 2012/05/03 10:58 # 삭제

    그런데 SSL과 함께 사용하면 매우 안전한 Communication이 되는것은 분명해보입니다.
  • 권남 2012/05/04 10:42 #

    문제는, 이 방식이 SSL 이 안 될때 사용하려고 만든 것이라는 거죠.
  • 바람처럼 2012/09/01 00:22 # 삭제

    잘 나가다가 히든폼에서 확 깨네요. onsubmit 을 이용하심이 어떨런지...
  • 권남 2012/09/01 19:40 # 삭제

    hidden form에 무슨 문제가 있기 때문에 onsubmit을 사용해야하는걸까요?
  • 못되먹은 사냥꾼 2015/10/16 10:52 #

    왜 히든폼에서 확 깨는지를 설명하지 않았네요.
  • answerjo 2013/03/08 09:22 # 삭제

    안녕하세요. 적용하다가 문제점이 좀 생겨서 질문 좀 드립니다.
    로컬에서 테스트할때는 아무 문제없이 잘 됬는데요
    서버에 적용을 시키니까
    예를들어 abcde 를 복호화하면
    ??@ㅅ?2;ㅌ?]-r??abcde
    다음과 같이 abcde 앞에 특수문자들이 붙습니다.ㅠㅠ
    소스 모두 인코등은 UTF-8이고요..혹시 어디가 문제인지 아실까해서요.
  • 권남 2013/03/09 15:09 #

    정확히 제가 겪은 문제랑 동일한지는 모르겠습니다만, 저기서 소개해드린 rsa javascript 라이브러리가 암호화시에 앞에 0 값이 들어간 한 바이트를 추가하는 것 같습니다.
    아직 저도 원인은 정확히 모르고요.
    복호화하기 전에 바이트배열에서 0값이 들어간 값을 제거하면 정상 작동합니다.
  • 네모 2014/10/13 21:07 # 삭제

    로컬 제우스환경에서는 잘 되었으나, 서버 제우스환경에서는 님과같이 복호화가 비정상적이였으나,
    위에 레퍼런스를 참고하여 다음과 같이 적용후 해결함.

    bcprov-jdk16-146.jar 추가 후

    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    //Cipher cipher = Cipher.getInstance("RSA");
    Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");

    이상입니다 ^^
  • 권남 2014/10/14 18:53 #

    적절한 정보 고맙습니다.
  • answerjo 2013/03/26 13:32 # 삭제

    답변 감사드립니다.
    will decrypt : 6380bd14475df7a08d405af3a6df8cf73be268c752f79ca153cb24ba793079de084717ae5abe7f1f0e46eb002457d5e6f4fc8409f331f8743b4c8e9b25d81738
    이값에서 0값을 모두 제거하면 되는건가요?
  • 권남 2013/03/26 18:47 #

    문자열에서 0을 제거하는 것이 아니라 저것을 byte 배열로 변경하면 맨 앞 byte의 값이 0인 경우가 가끔 생겼었습니다. 해당 바이트를 제거하면 잘 작동했었습니다.
  • answerjo 2013/03/27 12:47 # 삭제

    감사합니다.^^
  • answerjo 2013/05/06 19:12 # 삭제

    작업을 해서 패치를 해보았는데, 잘 안돼서 다시 한번 여쭤봅니다.

    byte[] encryptedBytes = hexToByteArrayRSA(securedValue);
    byte[] encryptedBytes2 = new byte[encryptedBytes.length-1]
    if(encryptedBytes[0] == 0){
    System.arraycopy(encryptedBytes, 1, encryptedBytes2, 0, encryptedBytes.length-1);
    }

    // 이런식으로 맨 앞값이 0인경우 encryptedBytes2[] 에 0값을 제거한 값을 복사한 후 encryptedBytes2[]를 복호화 하면 되는거 아닌가요?
    어렵게 찾은 RSA 방식인데, 이게 해결이 안되면, 다른 방안을 찾아야 해서요.
    혹시 아시는 부분이 있으면, 도움 부탁드립니다.
    좋은 하루 되세요.
  • 권남 2013/05/11 12:42 #

    해당 코드는 맞는것으로 보입니다.
    이게 안된다면 문제는 첫번째 바이트가 0이라서 생기는 것이 아니라 다른쪽에 있는 것 같습니다.
  • answerjo 2013/05/16 08:57 # 삭제

    답변 감사합니다.
    다른쪽을 좀더 테스트 해봐야겠네요^^
  • dowman 2013/08/19 11:40 # 삭제

    안녕하세요 좋은 정보 감사합니다.
    해당 소스를 받을 수 있을까요?
    링크가 깨져있어서 받을 수 가 없네요 ㅎㅎ
    부탁드립니다.
  • 권남 2013/08/19 13:37 #

    파일을 다시 올렸습니다.
    글 아랫부분의 RSATest.war를 다운로드해보시면됩니다.
  • 무중력고기 2013/11/04 15:42 # 삭제

    정말 도움이 많이 됐습니다.
    감사합니다~!
  • JUVE 2013/11/27 17:57 # 삭제

    좋은 글 감사합니다.
  • lover718 2013/12/11 16:01 # 삭제

    좋은글 감사합니다. 한가지 문제가 있는데 해결 가능한지 여쭙니다.
    복화화중 byte[] encryptedBytes = hexToByteArray(securedValue);
    이후에 byte[] decryptedBytes = cipher.doFinal(encryptedBytes); 이부분이서 에러가 발생합니다.
    javax.crypto.BadPaddingException: Blocktype mismatch: 0
    이 에러 원인이 뭔지 알수 있을까요?
  • 권남 2013/12/11 16:48 #

    위에 댓글 중에 첫번째 바이트가 0x0 으로 들어오는 문제를 읽어보세요.
  • lover718 2013/12/12 11:10 # 삭제

    으헥 RSATest.war 파일이 다운로드가 안되네요...
    HTML폼에 적용되는 스크립트중에 prng4.js, rng.js 도 꼭 필요한건가요?
  • 권남 2013/12/12 13:32 #

    저는 잘 되는데요, 일시적인 Egloos장애일 수 있으니 재시도 해보세요.
    아마 필요할 것 같은데 오래돼서 기억은 안납니다.
  • lover718 2013/12/12 15:35 # 삭제

    거참....희안하네요
    어제부터 오늘 오전까지 죽어라 에러라던게...지금은 갑자기 되네요..
    원인이 뭔지도 모르겠네요..건드린것도 없고..만진것도 없는데..
    아무튼 정보 감사합니다.
  • 김상현 2014/01/10 10:08 # 삭제

    너무 좋은자료 덕에 잘 보고 갑니다.
    퍼갔는데 혹시 문제가 된다면 쪽지주세요 삭제하겠습니다.
    감사합니다
  • hm 2014/03/25 18:04 # 삭제

    war 파일 받아서 그대로 실행하는데 ....이상하게 안되네요..ㅠ

    이거때문에 어제부터 계속 고민인데 .ㅠ 눈물납니다. 흙흙
  • 권남 2014/03/26 09:47 #

    오래된 글이라 저도 살펴보진 않았습니다만, 기본적인 작동 방식은 모두 블로그 글 안에 있으므로 읽고 이해하면 수정해서 적용 가능할 것으로 보입니다.
  • 김형조 2014/05/12 10:12 # 삭제

    감사합니다. 잘 보고 적용하려고 하는데요,
    저도 패스워드 앞에 이상한 쓰레기 문자가 붙어서;;
    로그인이 안되네요;;;
    왜 이럴까요;; utf-8도 맞는데..
  • 권남 2014/05/12 13:34 #

    댓글에 관련 이야기가 있습니다. 댓글들을 읽어보시고 다시 시도해보시기바랍니다.
  • 자바잡아 2014/06/11 14:14 # 삭제

    먼저 소스를 받아서

    파일 인코딩을 UTF-8로 모두 인코딩 했습니다.

    그리고, 위에 답글쓰신분 말처럼

    PrivateKey privateKey = (PrivateKey) session.getAttribute("rsaPrivateKey");
    ==>
    PrivateKey privateKey = (PrivateKey) session.getAttribute("__rsaPrivateKey__");

    하니 privateKey 가지고 오는 부분은 해결되더군요.

    좋은 정보 감사합니다.
  • 좋은날씨 2014/09/13 16:23 # 삭제

    로컬에서는 되는데, 서버에 적용하니,
    "로그인에 실패하였습니다. 자바스크립트가 지원되는 브라우저에서만 로그인 가능합니다."
    오류가 나네요.
  • 나모 2014/11/17 11:58 # 삭제

    비슷한게 있지 않을까 해서 찾아 왔는데
    잘 되네요
    잘쓸께요~~
  • 초보 2014/12/18 16:14 # 삭제

    안녕하세요.. JSP 암호화 관련해서 이런저런 글 찾다가 이 글을 보고 좋은 참고가 되었습니다..
    그런데 혹시
    javax.crypto.BadPaddingException: Data must start with zero 에 대한
    오류는 어떤 오류인지 알 수 있을까요?ㅠ..
  • 권남 2014/12/19 10:03 #

    위에 댓글들을 읽어보시면 비슷한 문제에 대처하신 분들 얘기가 있습니다.
  • 초보 2014/12/19 10:30 # 삭제

    아아// 그럼 이것도 첫 글자가 0일때 나는 오류인거군요..

    감사합니다ㅎ 감기 조심하셔요~
  • 초보 2015/01/13 17:51 # 삭제

    javax.crypto.BadPaddingException: Message is larger than modulus
    at sun.security.rsa.RSACore.parseMsg(RSACore.java:165)
    at sun.security.rsa.RSACore.crtCrypt(RSACore.java:121)
    at sun.security.rsa.RSACore.rsa(RSACore.java:84)
    at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
    at javax.crypto.Cipher.doFinal(DashoA13*..)
    at com.dreamsecurity.tsagwebadmin.util.RsaCrypto.decryptRsa(RsaCrypto.java:52)
    at egovframework.rte.dream.web.WebAdminController.login_check(WebAdminController.java:225)

    란 에러가 발생합니다. 무엇이 문제일까요..?
  • 권남 2015/01/13 21:25 #

    저도 모르겠습니다.
    이 글의 댓글들을 살펴보시면 이런저런 문제의 해결책들이 많이 나옵니다.
  • 레이고 2015/02/10 12:01 # 삭제

    안녕하세요 스트러츠 마이바티스에 적용을 해서 암호화를 해보려고 하는데 , 어떻게 하면 좋을까요 감이 안잡혀서요..
    초보 개발자라..조언좀 부탁드립니다.
  • 권남 2015/02/11 13:06 #

    질문이 무엇인지 감이 안 잡힙니다....
    요구사항을 명확히 정의하실 필요가 있어보입니다.
    이 블로그 글에서 논하고 있는 내용은 Struts나 마이바티스 같은 것과는 아무런 관련이 없습니다.
  • 레어 2015/03/19 17:37 # 삭제

    좋은 자료 감사합니다.
  • C.PARK 2015/04/20 15:42 # 삭제

    안녕하세요. 같은방식으로 Ajax 버전으로 만들어 테스트 완료 후 사용하려 합니다. 감사합니다. ^^

  • akdrp 2015/05/12 14:20 # 삭제

    도움이 되었습니다. 감사합니다.
  • 공짜 2015/08/10 16:48 # 삭제

    감사합니다. 도움이 정말 많이 되었습니다.

    저는 Node.js에서 구현을 해보았는데요.

    node-rsa라는 라이브러리를 사용해서 쉽게 구현할 수 있었습니다. ^^
  • 지나가다 2015/10/05 13:01 # 삭제

    엄밀히 말하거면 위법 아닌가요?

    제6조(개인정보의 암호화)
    ③ 정보통신서비스 제공자등은 정보통신망을 통해 이용자의 개인정보 및 인증정보를 송·수신할 때에는 안전한 보안서버 구축 등의 조치를 통해 이를 암호화해야 한다. 보안서버는 다음 각 호 중 하나의 기능을 갖추어야 한다.
    1. 웹서버에 SSL(Secure Socket Layer) 인증서를 설치하여 전송하는 정보를 암호화하여 송·수신하는 기능
    2. 웹서버에 암호화 응용프로그램을 설치하여 전송하는 정보를 암호화하여 송·수신하는 기능

  • 권남 2015/10/06 09:40 #

    법과는 무관합니다.
    위에 써놨듯이 이것은 SSL 대체용으로 만든게 아닙니다. SSL이 필요할 때는 SSL 쓰는게 맞아요.
    이것은 사내 서비스 처럼 대외적이 아닌 것이나, 법이 정하는 규모의 사이트(SSL 인증서 필수 사이트는 일방문자수 몇만 식으로 대규모 사이트만 해당합니다)가 아니면서 보안을 강화하고 싶을 때 사용하라는 것입니다.
  • 못되먹은 사냥꾼 2015/10/16 10:51 #

    법규만 적었지 왜 위법인지를 적지 않으셨네요.
  • 시크리트 2015/11/13 10:10 # 삭제

    저도 지나가다가 덧글 남깁니다.

    덧글로 법규 적어놓으신 것과 같이 해당 게시물의 암호화 방식은 2번 항목에 해당하는 보안 기법으로 법리적으로 해석했을때 합당한 조치입니다. 엄밀히 말하면 합법입니다.

    추가정보를 적자면 해당 코드가 컴파일된 파일은 응용프로그램에 속하고 웹서버에 설치되며 하이재킹 테스트를 했을때 전송하는 정보가 암호화 되어있기 때문입니다.
  • eds 2016/05/28 02:28 # 삭제

    이 예제는 사용자 등록시 비밀번호를 암호화하지 않은 채로 DB에 저장 한다는 건데, 서버가 털리면 모든 사용자 정보가 털린다는 점과 서버쪽에서 관계자가 열람할 수 있다는 문제가 있습니다.

    사용자 등록시 RSA 암호화 전에 사용자 비밀번호를 비대칭 encryption 하여 RSA 복호화 이후에도 읽을 수 없어야 합니다. 이후 로그인 시에는 보안 전송 후에 encryption 된 비밀번호 끼리 비교하면 됩니다.

    이 포스팅의 초점이 전송 보안이라서 부분은 간단히 설명한 것일 수 있지만 초보 개발자, 운영자들은 그 부분도 다 따라할 가능성이 있기에 노파심에 적습니다.
  • 초짜 2016/08/31 15:04 # 삭제

    진짜 초보적인건대 제가 모르는걸수도 있는데
    login.js 파일은 어디서 구하는건가요? 다른파일은 다 찾았는데.
  • 권남 2016/08/31 23:52 #

    본문에 있는 RSATest.war 파일을 다운 받아 압축을 풀면됩니다.
  • 나그네 2016/09/26 17:04 # 삭제

    좋은 포스팅 정말 감사합니다.
    그런데 글 내용중에
    .
    .

    사실 정말로 원한 것은 AES로 사용자의 정보를 암호화하고 AES키를 RSA공개키로 암호화하여 서버에 전달 한뒤, 서버에서 RSA 개인키로 AES키를 복호화해 알아내고, 그 AES키로 다시 사용자 정보를 복호화하는 이중 암호화를 시도했었다.
    .
    .
    이런게 있어서 AES가 뭔가 찾아봤는데 제가 이쪽분야는 지식이 짧아서 이해가 안가서 하나만 여쭤보려구요

    AES로 암호화에 사용한 키를 RSA의 퍼블릭키로 암호화를 해봤자 결국에 그 암호화된 키가 프론트단에 노출되어서
    RAS의 프라빗키로 복호화할필요도 없어 노출된 키로 사용자의 데이터를 복호화 할 수 있는게 아닌가요?
  • 권남 2016/09/27 10:25 #

    노출된 키는 사용자 브라우저의 메모리상에 존재하는 것입니다.
    여기서 막고자하는 것은 사용자 브라우져의 키가 웹 서버로 전달되는 과정의 "중간 네트워크를 가로채서 개인정보를 빼가는 것"을 막기 위한 것입니다.
    네트워크를 따라 흐를 때는 노출상태가 아니라 RSA Public Key로 암호화된 상태라서 중간에 가로채도 AES키를 알 수 없게 만드는게 목적이었습니다.
  • goblin 2016/12/28 14:29 # 삭제

    포스팅 감사합니다.
    그런데 아래 첨부파일 다운받아서 압축 해제후 테스트 해봤더니
    HTTP Status 500 - 암호화 비밀키 정보를 찾을 수 없습니다.
    java.lang.RuntimeException: 암호화 비밀키 정보를 찾을 수 없습니다.
    rsatest.LoginServlet.processRequest(LoginServlet.java:34)
    rsatest.LoginServlet.doPost(LoginServlet.java:117)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    이런 메시지가 나오는데
    혹시 해결방법을 알 수 있을까요....??
※ 로그인 사용자만 덧글을 남길 수 있습니다.