如何在Android上使用Java BouncyCastle API使用明文密钥对字符串进行RSA加密

如何在Android上使用Java BouncyCastle API使用明文密钥对字符串进行RSA加密,java,android,encryption,rsa,bouncycastle,Java,Android,Encryption,Rsa,Bouncycastle,我正在尝试使用Android中的BouncyCastleAPI对字符串进行加密,以发送到服务器 我有明文形式的公钥(当然是在内存中,而不是在文件系统中!不需要对我大喊大叫,密码学家;),我需要使用此明文公钥将字符串加密为RSA加密的字符串 这是我的班级: public class RSAEncryptor { //Get certificate from base64 string public static X509Certificate getCertificateFromBase64Str

我正在尝试使用Android中的BouncyCastleAPI对字符串进行加密,以发送到服务器

我有明文形式的公钥(当然是在内存中,而不是在文件系统中!不需要对我大喊大叫,密码学家;),我需要使用此明文公钥将字符串加密为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;
}
}

我目前没有得到正确加密的字符串,只是像这样乱七八糟:

��铷��%����我��Q��F*�屋宇署[@�Y�_H] T{KƾuTN�Q�
��U�F��]�s
�q | t�T�9�Rˇ�����)��{�},ޱ�ª�ǥ#���@k=�沃���F�7t“yP�Z�

是空字符)

我应该得到一个类似于以下内容的字母数字字符串:

2+tSXez8JrAIX+vj2dy4isa56xhwptwf8x2yggai6novucxknwykdyqjzicpmycqx75qbrgxwrw2ky9lmqr2xu17plqtukau2bna8wxytmjq7cwsn3sdabletrfsya+g3a2ro2qp6ar9ocbcfvjnzjjb9kaouj5pcj0tnpfdm=
(与实际响应相比有明显变化:D)

我将感谢任何帮助

谢谢


以前有人这样做过吗?我很想听听你对如何解决这个问题的建议。

代码中的问题:

String encrypted = new String(encryptedBytes);
坏主意!
Cipher#doFinal
返回一个
字节[]
,原因很好。它看起来像随机数据-将其转换为字符串肯定会造成混乱,因为平台默认编码(大多数情况下为UTF-8)将几乎肯定地解释随机字节是错误的。因此,如果您想从加密数据中获得字符串,则应该对字节数组进行Base64或Hex编码

根据您所说的预期,我会说您需要Base64编码的数据,所以您应该对密码的输出进行Base64编码

然后,加密字符串(这是真实的、人类可读的文本吗?)也不是最理想的。极易受攻击的是,减少的熵加上ECB模式的特征(RSA密码使用这种模式),大大降低了解决方案的安全性

纯RSA密码不应用于加密大于一个数据块(即大于RSA密钥的密钥大小)的数据,并且仅当数据是加密安全的随机数据时。在99%的情况下,这仅适用于用于对称密码(如AES等)的对称密钥

除了对称密钥包装和数字签名之外,不要使用RSA,在所有其他情况下,如果您想实际加密敏感数据,则使用对称密码,AES是一个不错的选择-128或256位实际上并不重要

工作流如下所示:

String encrypted = new String(encryptedBytes);
为AES生成对称密钥(如果使用AES-128/256,则为16/32字节)。现在,您可以使用服务器的公钥对该对称密钥进行RSA加密,而不使用其他加密方法,然后将密钥发送到服务器,然后使用AES和对称密钥对您的私有数据进行加密,服务器将使用其专用RSA密钥解密对称密钥,并解密您发送给它的数据包

使用TLS:

String encrypted = new String(encryptedBytes);
请注意,我使用的。以上只是故事的一部分。你刚刚发明的是一个关键传输协议。除非你是为生存而设计的,否则很有可能你不会在第一次尝试时获得这种安全性(如中间人攻击、反射攻击、重放攻击等)

这就是为什么在我看来,在客户端设备和服务器之间建立安全通道的广泛可用的安全选项是使用TLS(以前的SSL)。该协议是专门为单向(仅服务器)或双向身份验证(客户端和服务器)交换私有数据而设计的(在您的案例中,身份验证是使用RSA的一部分—配置“服务器证书”)

多年来,它已被加固,并经过多次修改,以抵御所有已知的对此类协议的攻击“已经被这个人或那个人破坏了,但是,如果你仔细设置它,对于没有广泛协议设计经验的普通人来说,它仍然是安全的


好的是,您只需在服务器上配置它(这比从头开始发明协议要容易得多),就可以在客户端和服务器之间使用完全加密的安全通信然后,使用TSL对您的客户端是完全透明的-他们只需将其访问URL从“http”更改为“https”-服务器的证书将被自动信任,因为它能够在证书路径中标识它,从而导致Java默认信任存储库中保存的一个根证书
cacerts

服务代码已经就位并为企业(数十亿美元的公司)运行而且它不会很快改变:P。我会检查你的建议,看看它们是否有效。谢谢!编辑:这是用来加密信用卡信息的。不能超过16-20个字符。它是通过SSL连接发送的RSA加密的,所以它是双重加密的。好的,是的,如果信用卡信息是随机生成的字符顺序,然后方法就可以了。我将更新帖子,使其更具体到这个用例中。我的假设是(错误的)您可能会尝试使用RSA加密更大的数据块。我很感激。我对加密相当陌生。我了解基本的公钥/私钥思想,但我从未实施过。这也是我开始使用RSA的原因。事实证明,我非常喜欢它,以至于在过去几年中几乎没有做过任何其他事情-希望您喜欢一半;)你介意我把它转换成聊天吗?如果你有时间的话,我有一些结果给你:)公钥之所以被称为公钥是因为它们是公开的