Java – How to RSA encrypt strings using plaintext keys using the Java BouncyCaSTLe API on Android

How to RSA encrypt strings using plaintext keys using the Java BouncyCaSTLe API on Android… here is a solution to the problem.

How to RSA encrypt strings using plaintext keys using the Java BouncyCaSTLe API on Android

I’m trying to encrypt a string to send to a server using the BouncyCaSTLe API in Android.

I have the public key in plaintext (in memory, of course, not in the file system!). Cryptographer, no need to yell at me; I need to encrypt the string to an RSA encrypted string using this plaintext public key.

This is my lesson :

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;
}

I don’t currently get properly encrypted strings, just gibberish like this :

��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�

(<?> is a null character).

I should get an alphanumeric string like this :

2+tSXez8JrAIX+VJ2Dy4IsA56XhWpTwF8X2yGGaI6novucXknwykDyqJZICpmYcqx75qBRgxwrW2kY9LmQR2xU17PLqTukAu2Bna8WXYTmJJQ7CWsN3SdABlETRfsYA+ g3A2rO2Qp6aR9OCBcFVJpnZJjb9kaOUj5Pcj0tNPFdM= (significantly different from the actual response :D).

I would appreciate any help!

Thanks!

Has anyone done it? I would like you to have any suggestions on how to fix this.

Solution

Issues in the code:

String encrypted = new String(encryptedBytes);

Bad idea! Cipher#doFinal returns a byte[] for good reason. It looks like random data – converting it to String is sure to cause confusion, as the platform’s default encoding (UTF-8 in most cases) almost certainly interprets random bytes as errors. So, if you want to get a string from encrypted data, then you should Base64 or hexadecimal encode the byte array.

Based on what you said you expect, I would say that you need Base64 encoded data, so you should Base64 encode the output of Cipher.

Then, encrypt the string (is this real, human-readable text?) Nor is it the best option. Extremely vulnerable, the reduced entropy combined with the characteristics of the ECB mode (used by RSA ciphers) greatly reduces the security of the solution.

Normal RSA

ciphers should not be used to encrypt data larger than one block (that is, greater than the size of the key of the RSA key), and only if the data is encrypted with secure random data. In 99% of cases, this only applies to symmetric keys used for symmetric ciphers such as AES.

RSA is useless except symmetric key wrapping and digital signatures, and in all other cases where you want to truly encrypt sensitive data, where you use symmetric ciphers, AES is a good choice – 128-bit or 256-bit doesn’t matter

The workflow will look like this:

Generate a symmetric key for AES (16/32 bytes if AES-128/256 is used). Now you will encrypt this symmetric key with the server’s public key RSA, then send the key to the server, then encrypt your private data with AES and the symmetric key, and the server will decrypt the symmetric key and decrypt the packets you send to it using its private key RSA key.

Use TLS:

Please note that I am using would. That’s just part of the story. What you just invented is a key protocol. And unless you design these to survive, you won’t get this security on your first attempt (like man-in-the-middle attacks, reflection attacks, replay attacks, etc.).

That’s why, in my opinion, the only widely used security option for establishing a secure channel between the client device and the server is to use TLS (formerly SSL). The protocol is specifically designed for exchanging private (private) data via one-way (server only) or two-way authentication (client and server) (authentication is the part where you use RSA in your case – configure “server certificates”).

It has been hardened over the years and revised several times to resist all known attacks on such protocols. And I know that every other day there is news about how “SSL” is broken by this or that person, but, if you set it up carefully, it is still safe because it is safe for ordinary people who don’t have extensive protocol design experience.

The benefit is that you simply configure it on the server (which is very easy compared to inventing a protocol from scratch) to be able to use fully encrypted secure communication between the client and server. If you set up a certificate/key pair on a server purchased from a “public” CA, then using TSL is completely transparent to your customers – they simply change the access URL from “http” to “https” – and the server’s certificate will be automatically trusted by being able to identify it in the certification path leading to one of the root certificates saved in the Java default truststore, cacerts.

Related Problems and Solutions