2011-08-29 39 views
3

我試圖加密使用Android中的BouncyCastle的API歡送到服務器的字符串明文密鑰字符串。我有明文公鑰(在內存中,當然不在文件系統中!不需要對我吼叫,密碼學家;)),我需要使用這個明文公鑰將字符串加密爲RSA加密字符串。我如何RSA加密使用Java API BouncyCastle的Android上

這是我的課:

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�

(該<?>「s爲空字符)

我應該得到類似以下內容的字母數字字符串:

2+tSXez8JrAIX+VJ2Dy4IsA56XhWpTwF8X2yGGaI6novucXknwykDyqJZICpmYcqx75qBRgxwrW2kY9LmQR2xU17PLqTukAu2Bna8WXYTmJJQ7CWsN3SdABlETRfsYA+g3A2rO2Qp6aR9OCBcFVJpnZJjb9kaOUj5Pcj0tNPFdM=(從實際反應明顯變化:d)

我會很感激任何幫助!

謝謝!

有沒有人做過這個?我很樂意提供有關如何解決此問題的建議。

+0

公鑰被稱爲是因爲它們是公開的。所以,請隨意將它們放在文件系統上,或放在紐約時報的首頁。 –

回答

4

問題中的代碼:

String encrypted = new String(encryptedBytes); 

壞主意! Cipher#doFinal有一個很好的理由返回byte[]。它看起來像隨機數據 - 變成一個字符串這將使一個爛攤子肯定的,因爲平臺的默認編碼(UTF-8在大多數情況下)將肯定地解釋隨機字節錯誤的差不多。所以如果你想從加密數據中得到一個字符串,那麼你應該對字節數組進行Base64或Hex編碼。

從你所說的你的期望我會說你想要Base64編碼的數據,所以你應該Base64編碼你的密碼的輸出。

然後,加密一個字符串(這是真實的,人類可讀的文本?)也不是最佳的。非常脆弱,降低熵加上ECB模式的特性(這由RSA密碼)降低解決方案的安全性顯着。

不應該使用普通的RSA密碼來加密大於一個塊(即大於RSA密鑰的密鑰大小)的數據,也不應該使用密碼安全的隨機數據。在所有情況的99%中,只給出了用於對稱密碼的對稱密鑰,例如AES等。

在所有其餘情況下,對於所有其他情況下的RSA密鑰封裝和數字簽名,您希望實際加密敏感數據,可使用對稱密碼,AES是一個不錯的選擇 - 128位或256位其實並不重要。

工作流程看起來像這樣:

生成AES對稱密鑰(16/32字節,如果您使用AES-128/256)。現在你使用服務器的公鑰RSA加密這個對稱密鑰,並沒有別的,並把密鑰發送給服務器,然後使用AES和對稱密鑰加密您的私人數據,該服務器將使用其私有RSA解密對稱密鑰鍵並解密您發送給它的數據包。

使用TLS:

注意我用的。以上只是故事的一部分。你剛剛發明的是一個關鍵傳輸協議。除非你正在爲生存而設計這些機會,否則你很難在第一次嘗試時獲得這種安全(如中間人攻擊,反射攻擊,重放攻擊......)。

這就是爲什麼我認爲只有廣泛可用的安全選項來建立客戶端設備和服務器之間的安全通道是使用TLS(前SSL)。該協議專門用於通過單向(僅服務器)或雙向身份驗證(客戶端和服務器)交換私有數據(身份驗證是您將使用RSA的部分 - 配置「服務器證書「)。

已硬化年和修訂了幾次,以承受這樣的協議的所有已知的攻擊。而且我知道,每隔一天就會有關於這個或那個人如何破壞「SSL」的消息,但是,如果仔細設置它,它對於沒有豐富的協議設計經驗的凡人來說也是安全的。

而且好處是,你只需要在服務器上配置它(相比於發明了一個從無到有的協議,這是很容易)能夠使用客戶端和服務器之間的完全加密的,安全的通信。如果您在從「公共CA」購買的服務器上設置證書/密鑰對,那麼使用TSL對您的客戶完全透明 - 他們僅將訪問URL從「http」更改爲「https」 - 服務器證書將通過能夠在證書路徑中識別它,自動信任它,該證書路徑導致保留在Java默認信任存儲庫cacerts中的一個根證書。

+0

不幸的是,服務代碼已經到位並且正在爲一家企業(數十億美元的公司)運行,並且它不會馬上改變:P。我會審查你的建議,看看他們的工作。謝謝!編輯:這是用來加密信用卡信息。不應超過16-20個字符。它是通過SSL連接發送的RSA加密的,所以它被雙重加密。 – Codeman

+0

好的,是的,如果信用卡信息是隨機產生的字符序列,那麼方法就OK了。我會更新這篇文章,使其更加具體到這個用例。我假設(錯誤地)你會嘗試使用RSA加密更大的數據塊。 – emboss

+0

我很感激!我對加密相當陌生。我瞭解基本的公共/私人關鍵想法,但我從來沒有實施過。 – Codeman