簡體   English   中英

如何使用Android上的Java BouncyCastle API使用明文密鑰對字符串進行RSA加密

[英]How do I RSA encrypt a string with a plaintext key using Java BouncyCastle API on Android

我正在嘗試使用Android中的BouncyCastle API加密字符串以發送到服務器。

我有明文的公鑰(在內存中,當然不在文件系統中!不需要對我大喊大叫,密碼學家;))我需要使用這個明文公鑰將字符串加密為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

<?>是空字符)

我應該找回一個與此類似的字母數字字符串:

2+tSXez8JrAIX+VJ2Dy4IsA56XhWpTwF8X2yGGaI6novucXknwykDyqJZICpmYcqx75qBRgxwrW2kY9LmQR2xU17PLqTukAu2Bna8WXYTmJJQ7CWsN3SdABlETRfsYA+g3A2rO2Qp6aR9OCBcFVJpnZJjb9kaOUj5Pcj0tNPFdM= (從實際的響應明顯改變:d)

我很感激任何幫助!

謝謝!

有沒有人這樣做過? 我很樂意你有任何關於如何解決這個問題的建議。

代碼中的問題:

String encrypted = new String(encryptedBytes);

餿主意! Cipher#doFinal返回一個byte[]是有充分理由的。 看起來像隨機數據 - 把它變成一個字符串肯定會弄得一團糟,因為平台默認編碼(在大多數情況下是UTF-8)幾乎可以肯定地解釋隨機字節錯誤。 因此,如果您希望從加密數據中獲取字符串,則應對字節數組進行Base64或Hex編碼。

根據您所說的您所期望的我會說您想要Base64編碼的數據,因此您應該對Cipher的輸出進行Base64編碼。

然后,加密字符串(這是真實的,人類可讀的文本嗎?)也不是最佳的。 高度脆弱,減少的熵加上ECB模式的特性(這由RSA密碼使用)大大降低了解決方案的安全性。

絕不應使用普通RSA密碼來加密大於一個塊的數據(即大於RSA密鑰的密鑰大小),並且僅當數據是加密安全隨機時才使用。 在所有情況的99%中,這僅用於對稱密碼,例如AES等。

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

工作流程這樣的:

為AES生成對稱密鑰(如果使用AES-128/256,則為16/32字節)。 現在,您使用服務器的公鑰對此對稱密鑰進行RSA加密,然后將密鑰發送到服務器,然后使用AES和對稱密鑰加密您的私有數據,服務器將使用其私有RSA密鑰解密對稱密鑰,解密發送給它的數據包。

使用TLS:

請注意我用更好些 以上只是故事的一部分。 你剛才發明的是密鑰傳輸協議。 除非你正在設計那些以生存為契機的東西,否則你在第一次嘗試時就不會得到這種安全感(如在中間人攻擊,反射攻擊,重放攻擊......)。

這就是為什么在我看來,在客戶端設備和服務器之間建立安全通道的唯一廣泛可用的安全選項是使用TLS(以前的SSL)。 該協議專門用於與單向(僅服務器)或雙向身份驗證(客戶端和服務器)交換私有數據(在您的情況下,身份驗證是使用RSA的部分 - 配置“服務器證書”) “)。

它經過多年的強化並經過多次修改以抵御所有已知的對這些協議的攻擊。 而且我知道,每隔一天都有關於“SSL”如何被這個或那個人打破的信息,但是,如果你仔細設置它,它就像沒有豐富的協議設計經驗的凡人一樣安全。

而且好處是你只需要在服務器上配置它(這與從頭開始創建協議相比非常容易),以便能夠在客戶端和服務器之間使用完全加密的安全通信。 如果您在服務器上設置了從“公共CA”購買的證書/密鑰對,則使用TSL對您的客戶完全透明 - 他們只是將訪問URL從“http”更改為“https” - 服務器的證書將通過能夠在證書路徑中識別它來自動信任,該證書路徑導致保存在Java的默認信任庫cacerts一個根證書。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM