2011-08-29 4 views
3

Android에서 BouncyCastle API를 사용하여 문자열을 암호화하여 서버로 보내려고합니다.Android에서 Java BouncyCastle API를 사용하여 RSA 평문 키로 문자열을 암호화하는 방법

나는 공개 키를 파일 시스템이 아니라 메모리에두고있다. 나는이 평문 공개 키를 사용하여 RSA에 문자열을 암호화해야한다. 암호화 된 문자열.

public class RSAEncryptor { 
//Get certificate from base64 string 
public static X509Certificate getCertificateFromBase64String(String string) 
     throws CertificateException, javax.security.cert.CertificateException 
{ 
    if(string.equals("") || string == null) { 
     return null; 
    } 

    byte[] certBytes = Base64.decode(string.getBytes(), Base64.DEFAULT); 

    X509Certificate cert = X509Certificate.getInstance(certBytes); 

    return cert; 
} 

//Get public key from base64 encoded string 
public static PublicKey getPublicKeyFromEncodedCertData(String encodedCertData) 
    throws CertificateException, javax.security.cert.CertificateException 
{ 
    if(encodedCertData == null || encodedCertData.equals("")) return null; 

    X509Certificate cert = getCertificateFromBase64String(encodedCertData); 

    if(cert == null) return null; 

    return cert.getPublicKey(); 
} 

public static String rsaEncrypt(String plainText, String keyFromResources) 
     throws NoSuchAlgorithmException, InvalidKeySpecException, 
     IOException, NoSuchPaddingException, InvalidKeyException, 
     IllegalBlockSizeException, BadPaddingException // 
{ 
    if(plainText == null || plainText.equals("")) return null; 
    if(keyFromResources == null || keyFromResources.equals("")) return null; 

    byte[] encryptedBytes; 

    Cipher cipher = Cipher.getInstance("RSA"); 
    PublicKey publicKey = null; 

    try { 
     publicKey = getPublicKeyFromEncodedCertData(keyFromResources); 
    } 
    catch(Exception ex) { 
     Logger.LogError("getPublicKeyFromEncodedCertData()", ex); 
    } 

    cipher.init(Cipher.ENCRYPT_MODE, publicKey); 

    encryptedBytes = cipher.doFinal(plainText.getBytes()); 
    String encrypted = new String(encryptedBytes); 
    return encrypted; 
} 

} 나는 현재하고, 다시 밖으로 같은 단지 왜곡 엉망 적절하게 암호화 된 문자열을받지 못했습니다

:

��RB��%����I��Q��F*�bd[@�y�_H]T{KƾuTN�Q� ��U�f��]�S �q|.t�t�9�Rˇ�����)��{�},ޱ�ª�ǥ#���@k=�WO���f�7t"yP�z�

내 클래스 ( <?>의 문자가 null입니다.)

2+tSXez8JrAIX+VJ2Dy4IsA56XhWpTwF8X2yGGaI6novucXknwykDyqJZICpmYcqx75qBRgxwrW2kY9LmQR2xU17PLqTukAu2Bna8WXYTmJJQ7CWsN3SdABlETRfsYA+g3A2rO2Qp6aR9OCBcFVJpnZJjb9kaOUj5Pcj0tNPFdM= (실제 응답에서 분명히 변화 : D) 내가 어떤 도움을 주셔서 감사합니다 것

나는이 비슷한 영숫자 문자열을 다시 받고해야한다!

감사합니다.

이전에이 작업을 수행 한 사람이 있습니까? 이 문제를 해결하는 방법에 대한 제안이 있으면 좋겠습니다. 코드에서

+0

공개 키는 공개 키이기 때문에 공개 키라고합니다. 파일 시스템이나 New York Times의 첫 페이지에 자유롭게 놓으십시오. –

답변

4

문제 :

String encrypted = new String(encryptedBytes); 

나쁜 아이디어! Cipher#doFinal은 적절한 이유로 byte[]을 반환합니다. 무작위 데이터처럼 보입니다. 이것은 플랫폼 기본 인코딩 (대부분의 경우 UTF-8)이 랜덤 바이트를 거의 확실하게 잘못 해석 할 것이기 때문에 이것을 String으로 변환하면 엉망이됩니다. 따라서 암호화 된 데이터에서 String을 가져 오려면, 바이트 배열을 Base64- 또는 Hex- 인코딩해야합니다.

기대했던대로 Base64로 인코딩 된 데이터를 원한다고 말하면서 Cipher의 출력을 Base64로 인코딩해야합니다.

문자열을 암호화하면 (사람이 읽을 수있는 실제 텍스트입니까?) 또한 최적이 아닙니다. 높은 취약성, 감소 된 엔트로피 및 ECB 모드의 특성 (RSA 암호에 의해 사용됨)은 솔루션의 보안을 크게 낮 춥니 다.

일반 RSA 암호는 한 블록 (즉, RSA 키의 키 크기보다 큼)보다 큰 데이터를 암호화하는 데 사용해서는 안되며 데이터가 암호로 무작위로 안전한 경우에만 사용해야합니다. 모든 경우의 99 %에서 AES 등의 대칭 암호에 사용되는 대칭 키에만 사용됩니다.

대칭 키 래핑 및 디지털 서명 이외에는 RSA를 사용하여 실제로 민감한 데이터, 대칭 암호를 사용하면 AES가 좋은 선택입니다. 128 또는 256 비트는 별 문제가되지 않습니다.

워크 플로 다음과 같을 것이다 :

는 AES를위한 대칭 키를 생성 (16/32 바이트는 AES-256분의 128을 사용하는 경우). 이제 이 대칭 키를 RSA 암호화하고 서버의 공개 키를 사용하여 RSA로 암호화 한 다음 서버에 공개 키를 보낸 다음 AES와 대칭 키를 사용하여 개인 데이터를 암호화하면 서버는 개인 RSA를 사용하여 대칭 키를 해독합니다 키를 누르고 전송 한 패킷의 암호를 해독하십시오.

사용 TLS :

주 내 사용. 위는 이야기의 일부일뿐입니다. 방금 발명 한 것은 키 전송 프로토콜입니다. 그리고 당신이 살아있는 사람들을 위해 디자인하지 않는 한, Man-In-The-Middle-Attacks, Reflection Attack, Replay Attack 등과 같이 처음 시도 할 때는 안전하지 못할 가능성이 높습니다.

클라이언트 장치와 서버간에 보안 채널을 설정하기 위해 널리 사용되는 보안 옵션은 TLS (이전 SSL)를 사용하는 것입니다. 이 프로토콜은 개인 데이터를 단방향 (서버 전용) 또는 양방향 인증 (클라이언트 및 서버)으로 교환하기 위해 특별히 설계되었습니다 (인증은 RSA를 사용하려는 경우 - 서버 인증서 구성 ").

수년 동안 강화되어 이러한 프로토콜에 대한 모든 알려진 공격에 견딜 수 있도록 몇 차례 개정되었습니다. 그리고 제가 알고 있듯이, "SSL"이이 사람이나 그 사람에 의해 어떻게 망가 졌는지에 대한 메시지가 있습니다. 그러나 당신이주의 깊게 설정하면 프로토콜 설계에 대한 광범위한 경험이없는 단순한 필사자만큼 안전합니다.

좋은 점은 클라이언트와 서버 모두에서 완벽하게 암호화되고 안전한 통신을 사용할 수 있도록 서버에서 프로토콜을 구성하기 만하면됩니다 (처음부터 프로토콜을 발명하는 것과 비교하면 매우 쉽습니다). "공용 CA"에서 구입 한 서버에 인증서/키 쌍을 설정하면 TSL을 사용하는 것은 클라이언트에게 완전히 투명하게됩니다 - 단지 액세스 URL을 'http'에서 'https'로 변경하기 만하면 서버의 인증서가 Java의 기본 트러스트 스토어 cacerts에 보관 된 루트 인증서 중 하나로 연결되는 인증서 경로에서 인증서를 식별함으로써 자동으로 신뢰할 수 있습니다.

+0

불행하게도 서비스 코드는 이미 기업 (수십억 달러짜리 회사)에서 운영되고 있으며 곧 변경 될 것입니다 : P. 나는 당신의 제안을 복습하고 그것이 효과가 있는지 살펴볼 것입니다. 감사! 편집 : 이것은 신용 카드 정보를 암호화하는 데 사용됩니다. 16-20자를 초과해서는 안됩니다. SSL 연결을 통해 전송 된 RSA 암호화이므로 ​​이중 암호화됩니다. – Codeman

+0

예, 신용 카드 정보가 임의로 생성 된 문자 시퀀스 인 경우 접근 방법은 정상입니다. 이 유스 케이스에보다 구체적으로 만들기 위해 게시물을 업데이트 할 것입니다. 나는 당신이 RSA로 더 많은 데이터를 암호화하려고 시도 할 것이라고 잘못 생각했다. – emboss

+0

감사합니다. 나는 암호화가 처음 인 사람이다. 나는 기본적인 공개/개인 키 아이디어를 이해하지만 결코 구현하지 않아도된다. – Codeman

관련 문제