我正在尝试使用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或Hex编码.
根据您的预期,我会说您希望使用Base64编码的数据,因此您应该使用Base64编码Cipher的输出.
然后,对字符串进行加密(这是真实的人类可读文本吗?)也不是最佳选择.高度脆弱的信息熵以及ECB模式的特征(由RSA密码使用)降低了安全性,从而大大降低了解决方案的安全性.
单纯的RSA密码永远不要用于加密大于一个块的数据(即大于RSA密钥的密钥大小),并且仅当数据是加密安全的随机数据时才可以使用.在所有情况的99%中,仅针对用于对称密码的对称密钥(例如AES等)提供此密码.
将RSA用于对称密钥包装和数字签名,在要真正加密敏感数据的所有其余情况下,请使用对称密码,AES是一个不错的选择-128位或256位并不重要.
工作流程如下所示:
为AES生成对称密钥(如果使用AES-128 / 256,则为16/32字节).现在,您将使用服务器的公共密钥对这个对称密钥进行RSA加密,然后再将其发送到服务器,然后使用AES和对称密钥对您的私有数据进行加密,服务器将使用其私有RSA密钥对对称密钥进行解密,并且解密您发送给它的数据包.
使用TLS:
请注意我对will的使用.以上只是故事的一部分.您刚刚发明的是一个密钥传输协议.而且,除非您为生活而设计这些东西的机会很高,否则您就不会在初次尝试时获得这种安全感(例如,在中间人攻击,反射攻击,重播攻击等方面).
这就是为什么我认为在客户端设备和服务器之间建立安全通道的唯一广泛可用的安全选项是使用TLS(以前的SSL).该协议是专门为通过单向(仅服务器)或双向认证(客户端和服务器)交换私有数据而设计的(身份验证是您在这种情况下将使用RSA的部分-配置“服务器证书” ”).
它经过了数年的强化和修改,可以抵抗这种协议的所有已知攻击.而且我知道,每隔一天就会有关于此人如何破坏“ SSL”的消息,但是,即使您仔细地进行设置,它也像没有协议设计经验的凡人一样安全.
令人高兴的是,您只需要在服务器上对其进行配置(与从头开始创建协议相比,这非常容易),以便能够在客户端和服务器之间使用完全加密的安全通信.如果您在从“公共CA”购买的服务器上设置证书/密钥对,则使用TSL对您的客户端完全透明-他们只将其访问URL从“ http”更改为“ https”-服务器的证书将通过能够在证书路径中进行标识而自动获得信任,该证书路径导致Java的默认信任库cacerts中保留的根证书之一.