我正在尝试使用 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[]
是有充分理由的。它看起来像随机数据 - 将其转换为 String 肯定会造成困惑,因为平台默认编码(大多数情况下为 UTF-8)几乎可以肯定地将随机字节解释为错误。因此,如果您想从加密数据中获得一个字符串,那么您应该对字节数组进行 Base64 或十六进制编码。
根据您所说的您所期望的,我会说您需要 Base64 编码的数据,因此您应该对 Cipher 的输出进行 Base64 编码。
然后,加密字符串(这是真实的、人类可读的文本吗?)也不是最佳选择。极易受到攻击,减少的熵加上 ECB 模式的特征(RSA 密码使用此模式)会大大降低解决方案的安全性。
不应使用普通 RSA 密码来加密大于一个 block (即大于 RSA key 的 key 大小)的数据,并且仅当数据是加密安全随机数据时才使用。在 99% 的所有情况下,这仅适用于用于 AES 等对称密码的对称 key 。
除了对称 key 包装和数字签名外,RSA 别无他用,在您想要真正加密敏感数据的所有其他情况下,您使用对称密码,AES 是一个不错的选择 - 128 位或 256 位并不重要.
工作流程将如下所示:
为 AES 生成对称 key (如果使用 AES-128/256,则为 16/32 字节)。现在您将使用服务器的公钥 RSA 加密此对称 key ,然后将 key 发送到服务器,然后使用 AES 和对称 key 加密您的私有(private)数据,服务器将解密对称 key 使用其私钥 RSA key 解密您发送给它的数据包。
使用 TLS:
请注意我使用的是would。以上只是故事的一部分。你刚刚发明的是一个 key 传输协议(protocol)。并且除非您设计这些是为了生存,否则您在第一次尝试时就不会获得这种安全性(如中间人攻击、反射攻击、重放攻击等)。
这就是为什么在我看来,在客户端设备和服务器之间建立安全通道的唯一广泛使用的安全选项是使用 TLS(以前的 SSL)。该协议(protocol)专门用于通过单向(仅服务器)或双向身份验证(客户端和服务器)交换私有(private)数据(身份验证是您在您的情况下使用 RSA 的部分 - 配置“服务器证书” ”)。
它经过多年强化并经过多次修订,以抵御对此类协议(protocol)的所有已知攻击。而且我知道,每隔一天就会有关于“SSL”如何被这个或那个人破坏的消息,但是,如果你仔细设置它,它仍然是安全的,因为它对于没有丰富协议(protocol)设计经验的普通人来说是安全的。
好处是您只需在服务器上配置它(与从头开始发明协议(protocol)相比,这非常容易)就能够在客户端和服务器之间使用完全加密的安全通信。如果您在从“公共(public) CA”购买的服务器上设置证书/ key 对,那么使用 TSL 对您的客户来说是完全透明的——他们只需将访问 URL 从“http”更改为“https”——服务器的证书将通过能够在通向 Java 默认信任库 cacerts
中保存的根证书之一的证书路径中识别它来自动信任。
关于java - 如何在 Android 上使用 Java BouncyCaSTLe API 使用明文 key 对字符串进行 RSA 加密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7234529/