Android Code Snippet [B4X] RSA Encrypt and Decrypt

Hey, i build a auth method for my app to protect my API and one of this step is to signed requests to the api to ensure that every request comes from my app. RSA is the best method for that, because this is not a hash, so that the same input is ever the same output. The API knows the Private Key, the apps knows the public key.

On B4J you need the "bcprov-jdk15on-161.jar" you can download it here.

Require libs.
B4A:
  • ByeConverter
  • Encryption
  • StringUtils
B4J:
  • ByeConverter
  • Encryption
  • jStringUtils
B4I:
  • iRandomAccessFile
  • iRSA
  • iStringUtils
The Private Key you can generate on this website. 1024 Bit, the keys are PKCs1 and the iRSA lib. on B4I needs a PKCs8 key, but to encrypt the PKCs1 with the public key it works, decrypt needs a PKCs8 key, but then it doesnt work with this code on B4J and B4A ;)

B4X:
Sub Button1_Click
   
Dim msg As String = "Test123!"
   
    Dim privatekey2 As String = "MIICXAIBAAKBgQCbxeEx0hYmBFlz7l3Lh6HeA4YkgCoJgwG2FJ5KCkZ8k21YJslzgLf9NwrjXjqRwN311E5FAH/nXTPLddjnh0mAQiaMgGf/+w1lRbn1gjIYAUMK9FsVuZJ+POu8e6TgRTFTiz1/BLu0trZaH/XqqnnUjsjEXd63VVdepZ62vxfVywIDAQABAoGAQ7qa+n188dSsTDLVB1yWraBcn9w16uLSSKfYVxr2oM29GjnrF1RdKzTWgBuFXcA9AdjomynncuJcVdeMksaI1xCnS0J6ge4fd1dWMqRZoJo7pBUWrqUaaAWCiSOmhf+5e6MnbdKQTR+mmBUSNnmqPMZ4nHBwrcuxzAAzE8rKKFkCQQD4J4YtyFHRdaQ38YZS0N1SXLZ9RNFRZzopHx7kTlhW5AwHXOCKC1Zef/ZlWMdHjENY0Op4eXu6oQhh5jtBtdXdAkEAoLKm9+DNSVa7hX2GkMqO9rKrPo/Cu7qhymBzdFI/rdZM8dv+FBJ8Z9RJTQq6wm5LpB4v0/A7TfTS2umGuuEDxwJAFtUcJW4/CPS4DWWtpEUPeBqLt+7zC3hiA5KXdw42VphY+vxytIDii4NemmiWvWCMecmPKVKULdHPQaK7ZQpkCQJABbfVsSRSqbVoXsukqipLBAQ/i65Z44w0jZr5AL6cfXcOrdyCIfy2aItpQFCNbLW4A1L/qK73rUJ07k04+hVfqQJBAKC8P7K7laB+KQcqcJ0LLWsZWrbFtQq9XoqflKMtG5K9FHfMpblPiqgh9v6a83xr0Di+NyCl4ibiIE8ghXs9wx0="
    Dim publickey2 As String = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbxeEx0hYmBFlz7l3Lh6HeA4YkgCoJgwG2FJ5KCkZ8k21YJslzgLf9NwrjXjqRwN311E5FAH/nXTPLddjnh0mAQiaMgGf/+w1lRbn1gjIYAUMK9FsVuZJ+POu8e6TgRTFTiz1/BLu0trZaH/XqqnnUjsjEXd63VVdepZ62vxfVywIDAQAB"
   
    Dim output As String = EncryptRSAWithPublicKey(msg,publickey2)
   
    Log(output)
    Log(DecryptRSAWithPrivateKey(output,publickey2))
   
End Sub

Private Sub EncryptRSAWithPublicKey(Text As String,PublicKey As String) As String
   
    #If B4I
    Dim su As StringUtils
    Dim rsa As RSA
    Return su.EncodeBase64(rsa.EncryptWithPublicKey(Text.GetBytes("UTF8"),PublicKey))
    #Else
   
    Dim su As StringUtils
    Dim pubkey() As Byte = su.DecodeBase64(PublicKey)
    Dim Enc As Cipher
    Enc.Initialize("RSA/ECB/PKCS1Padding")
    Dim kpg As KeyPairGenerator
    kpg.Initialize("RSA", 1024)
    kpg.PublicKeyFromBytes(pubkey)
    Dim bytes() As Byte = Text.GetBytes("UTF8")

    Return su.EncodeBase64(Enc.Encrypt(bytes,kpg.PublicKey,False))
   
    #End If
   
End Sub

Private Sub DecryptRSAWithPrivateKey(encryptedstring As String,PrivateKey As String) As String
   
    #If B4J
   
    #AdditionalJar: bcprov-jdk15on-161.jar

    #if java
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import java.security.Provider;
    import java.security.Security;
    static{
     Provider BC = new BouncyCastleProvider();
     Security.addProvider(BC);
    }
    #End If
   
    #End if
   
    #If B4A or B4J
   
    Dim su As StringUtils
    Dim privkey() As Byte = su.DecodeBase64(PrivateKey)
    Dim kpg As KeyPairGenerator
    kpg.Initialize("RSA", 1024)
    kpg.PrivateKeyFromBytes(privkey)
    Dim Enc As Cipher
    Enc.Initialize("RSA/ECB/PKCS1Padding")
    Dim bc As ByteConverter
    Return bc.StringFromBytes(Enc.Decrypt(su.DecodeBase64(encryptedstring),kpg.PrivateKey,False),"UTF8")
   
    #End If
   
End Sub

6h of work... in this time I would have written 5 XUI views ^^
 

b4x-de

Active Member
Licensed User
Longtime User
Dear Alexander,

Thank you very much for your example, that serves for me as great starting point to understand and integrate RSA into my apps.

When I was using the code you provided in your original code I found a small typo in Sub Button1_Click(). I changed the public key in Log(DecryptRSAWithPrivateKey(output,publickey2)) to private key in Log(DecryptRSAWithPrivateKey(output,privatekey2)).

Running the code I receive in Sub DecryptRSAWithPrivateKey() the following exception, you have mentioned already in your original post:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0890ba:ASN.1 encoding routines:asn1_check_tlen:WRONG_TAG

First it seemed to me the private key is in PKCS#1, therefore the Cipher is initialized with "RSA/ECB/PKCS1Padding" and the KeyPairGenerator expects the private key to be in PKCS#8 when calling kpg.PrivateKeyFromBytes(privkey) which results in an error.

On the Website you are pointing to generate the pair of public and private key (http://travistidwell.com/jsencrypt/demo/) the private key is generated in PKCS#1. When you have a look at the first header line you find: -----BEGIN RSA PRIVATE KEY----- for PKCS#1. If we need PKCS#8 the line should be -----BEGIN PRIVATE KEY-----.

But after digging deeper it turned out to be a simple conversion problem explained by KMatle in this post: https://www.b4x.com/android/forum/threads/using-rsa-in-b4a-to-communicate-with-php-openssl.59445/ Based on the algorithm he describes in this post and the library RSAKeyConverter he provides I wrote the following function:

B4X:
Public Sub convertOpenSSL2b4x(pstrOpenSslRsaKey As String) As Byte()

            Dim su As StringUtils
            Dim bc As ByteConverter

            Dim strOpenSslKey As String
            Dim bytOpenSslKey() As Byte

            Dim bytB4xKey() As Byte

            bytOpenSslKey = su.DecodeBase64(pstrOpenSslRsaKey)
            strOpenSslKey = bc.StringFromBytes(bytOpenSslKey, "UTF8")

            ' in case it's a public key
            strOpenSslKey = strOpenSslKey.Replace("-----BEGIN PUBLIC KEY-----", "")
            strOpenSslKey = strOpenSslKey.Replace("-----END PUBLIC KEY-----", "")

            ' in case it's a private key in PKCS#1
            strOpenSslKey = strOpenSslKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
            strOpenSslKey = strOpenSslKey.Replace("-----END RSA PRIVATE KEY-----", "")

            ' in case it's a private key in PKCS#8
            strOpenSslKey = strOpenSslKey.Replace("-----BEGIN PRIVATE KEY-----", "")
            strOpenSslKey = strOpenSslKey.Replace("-----END PRIVATE KEY-----", "")

            bytB4xKey = su.DecodeBase64(strOpenSslKey)

            Return bytB4xKey
End Sub

You may use it in your code to convert the key you receive as a string parameter to a byte array:
B4X:
pubkey = convertOpenSSL2b4x(PublicKey)
privkey = convertOpenSSL2b4x(PrivateKey)

I made the changed to your code which results in the following complete example that works for me on B4A 8.00 with BytConverter 1.10, Encryption 1.10 and StringUtils 1.12:

B4X:
Private Sub test()
       
   Dim msg As String = "Test123!"
       
   Dim privatekey2 As String = "MIICXAIBAAKBgQCbxeEx0hYmBFlz7l3Lh6HeA4YkgCoJgwG2FJ5KCkZ8k21YJslzgLf9NwrjXjqRwN311E5FAH/nXTPLddjnh0mAQiaMgGf/+w1lRbn1gjIYAUMK9FsVuZJ+POu8e6TgRTFTiz1/BLu0trZaH/XqqnnUjsjEXd63VVdepZ62vxfVywIDAQABAoGAQ7qa+n188dSsTDLVB1yWraBcn9w16uLSSKfYVxr2oM29GjnrF1RdKzTWgBuFXcA9AdjomynncuJcVdeMksaI1xCnS0J6ge4fd1dWMqRZoJo7pBUWrqUaaAWCiSOmhf+5e6MnbdKQTR+mmBUSNnmqPMZ4nHBwrcuxzAAzE8rKKFkCQQD4J4YtyFHRdaQ38YZS0N1SXLZ9RNFRZzopHx7kTlhW5AwHXOCKC1Zef/ZlWMdHjENY0Op4eXu6oQhh5jtBtdXdAkEAoLKm9+DNSVa7hX2GkMqO9rKrPo/Cu7qhymBzdFI/rdZM8dv+FBJ8Z9RJTQq6wm5LpB4v0/A7TfTS2umGuuEDxwJAFtUcJW4/CPS4DWWtpEUPeBqLt+7zC3hiA5KXdw42VphY+vxytIDii4NemmiWvWCMecmPKVKULdHPQaK7ZQpkCQJABbfVsSRSqbVoXsukqipLBAQ/i65Z44w0jZr5AL6cfXcOrdyCIfy2aItpQFCNbLW4A1L/qK73rUJ07k04+hVfqQJBAKC8P7K7laB+KQcqcJ0LLWsZWrbFtQq9XoqflKMtG5K9FHfMpblPiqgh9v6a83xr0Di+NyCl4ibiIE8ghXs9wx0="
   Dim publickey2 As String = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbxeEx0hYmBFlz7l3Lh6HeA4YkgCoJgwG2FJ5KCkZ8k21YJslzgLf9NwrjXjqRwN311E5FAH/nXTPLddjnh0mAQiaMgGf/+w1lRbn1gjIYAUMK9FsVuZJ+POu8e6TgRTFTiz1/BLu0trZaH/XqqnnUjsjEXd63VVdepZ62vxfVywIDAQAB"
   Dim privatekey1 As String ="MIICWgIBAAKBgF4uzIWRQr/hg4Shzetmu1uFievGW6IBMkYOFHdetnpggIZJxdw5kU2qZIIN9N4msU8qxQ2oObyeXRunz8xiRd7s33LtwziuXKcWR70rffi7mDMWAYK4YQBoYGEJMfCmHmQomo6kcpyuoawmxz5YMmQNRJq7XNbMrOyhVutcRlW7AgMBAAECgYBJsDv81SZwkrUf9Kj0YTB97wF2I4BWRLFc9m805jnHjfGBH/9ayv/Q0FxLFThdB9D6q+/5O4HQ/ebDWIzQ0KLxUI6GwF4lb/1nOAw2EAWdlg0pBP/O6qbRlNKpfbPkzYbcyodmNGjrMBJ+gm5rzldaJ9GpYvkx+GGc/IIQSh9zIQJBAKgK1BEw8qYzpK1AUu0jify8XoHU/lLzI1xAbyu5Dej3JceC2UDlye8pX4PEXxpPL/YeOS4d3zDlaUSzDTqoMxUCQQCPewRbQkNntERD6Cu8NUJ+LlUjHhlXwsGcITs25H5SIkJtIj/N6qVezhRBfcdGBQfhVKqUhbKQKJ9FFG/GMdmPAkB+HfJfHJ9gQW4cYSRbucp8FlvPkS12z+f/+pP0+qQH7kU6YWfwHwXZKvH2kNDXjArw6mn7xIuXxPWmi8Mn/MipAkAo2slpxtt0mwLZJzHU3IF7dmvtnPNwZtdEQr4eqj9B2dl6Q8dRNeAyt5FS+KKWUHZm9fnAYrVukPKQ8/nDwIwHAkBrktDvs02dg3mHe52fQwpttPtUL5w1wMnAIbSu/eudQlRoTjee0G+MUqjhoerMZ5cHn/GEkDPH+g1DfhAeAznI"
   Dim publickey1 As String ="MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgF4uzIWRQr/hg4Shzetmu1uFievGW6IBMkYOFHdetnpggIZJxdw5kU2qZIIN9N4msU8qxQ2oObyeXRunz8xiRd7s33LtwziuXKcWR70rffi7mDMWAYK4YQBoYGEJMfCmHmQomo6kcpyuoawmxz5YMmQNRJq7XNbMrOyhVutcRlW7AgMBAAE="
   Dim privatekey As String =   "-----BEGIN RSA PRIVATE KEY-----" & _
                               "MIICWwIBAAKBgQCK8HsbGe8BEdHgxD66rk5qYvH8brZD7U6UHOOnOV1JxiC5vhv/" & _
                               "GbJQXUgtktmXArYhuAol7ifQRiFumhmjLIVcN/XI3hTMRtT5hehKjbe3syTGXyiW" & _
                               "8ibjhEleza74XUHuJQU0fK2rhERrbqida7q5M9yOERZMqr6AOBB9UIHE4wIDAQAB" & _
                               "AoGAF3KGFAjIYPHU7Y2w2GhaK3IPTH6Sm9wMisplUO9JYw3gr/f5WY/Im+9iIu7J" & _
                               "ccavXfllHQmYBnoPYk73TKZSywEUq7AnLIN8kggb/ebIGyRwcYsi1ILSjGSHjHGE" & _
                               "sQWcnakNrZClJS1tMwfxuvDbVnn6+LFPUXE+sV7m1sxERyECQQDYYHchNARezrVq" & _
                               "u56u/mVk5qCtRz5gKof5sN/DQ/cy7lF1yM6oyQzwAOqgf6v/TD6Q7lbaL+g4u1c6" & _
                               "unIP/JztAkEApGHS4EWvH9QkVV07f8bjGOxBY0QkrxRbAdkBj+aMRMStIExydUzU" & _
                               "OC2kbKY80jkqPcsQUmXRxrmt0D3uqed/DwJATd8ZwKdZ000NWWZAi7Yeg5vEw9fa" & _
                               "CP4FhcU0IrA5LOk8A+nLBk+lClMqHa0VF4I3Gi3MsCWBq7kwgYBMQJinWQJAYTWI" & _
                               "YyBSY9wscF7mJLYU7lQohu9xUigTeZA/ID4SjUhRqVwMA9ofVemH5siYBynPFSPU" & _
                               "zyVvXLnJU/CMBuXjeQJAEWSH6U2GrruPLhZqy6ic2N148KB3N3E+kAc+ZhU+Yuxc" & _
                               "f5HELRaeR/Alaz+wPXNuI4wv69heGaHz3RizIxHeDg==" & _
                               "-----END RSA PRIVATE KEY-----"
   Dim publickey As String =   "-----BEGIN PUBLIC KEY-----" & _
                               "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK8HsbGe8BEdHgxD66rk5qYvH8" & _
                               "brZD7U6UHOOnOV1JxiC5vhv/GbJQXUgtktmXArYhuAol7ifQRiFumhmjLIVcN/XI" & _
                               "3hTMRtT5hehKjbe3syTGXyiW8ibjhEleza74XUHuJQU0fK2rhERrbqida7q5M9yO" & _
                               "ERZMqr6AOBB9UIHE4wIDAQAB" & _
                               "-----END PUBLIC KEY-----"
   Dim su As StringUtils
   Dim publicKey64 As String = su.EncodeBase64(publickey.GetBytes("UTF8"))
   Dim privateKey64 As String = su.EncodeBase64(privatekey.GetBytes("UTF8"))

   
   Dim output As String = EncryptRSAWithPublicKey(msg,publicKey64)
    Log(msg)
   Log(output)
   Log(DecryptRSAWithPrivateKey(output,privateKey64))
       
End Sub

Private Sub EncryptRSAWithPublicKey(Text As String,PublicKey As String) As String
       
        #If B4I
        Dim su As StringUtils
        Dim rsa As RSA
        Return su.EncodeBase64(rsa.EncryptWithPublicKey(Text.GetBytes("UTF8"),PublicKey))
        #Else
   
   Dim su As StringUtils
   Dim pubkey() As Byte
   pubkey = convertOpenSSL2b4x(PublicKey)
   Dim Enc As Cipher
   Enc.Initialize("RSA/ECB/PKCS1Padding")
   Dim kpg As KeyPairGenerator
   kpg.Initialize("RSA", 1024)
   kpg.PublicKeyFromBytes(pubkey)
   Dim bytes() As Byte = Text.GetBytes("UTF8")

   Return su.EncodeBase64(Enc.Encrypt(bytes,kpg.PublicKey,False))
       
        #End If
       
End Sub

Private Sub DecryptRSAWithPrivateKey(encryptedstring As String,PrivateKey As String) As String
       
    #If B4J
   
    #AdditionalJar: bcprov-jdk15on-161.jar

    #if java
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import java.security.Provider;
    import java.security.Security;
    static{
     Provider BC = new BouncyCastleProvider();
     Security.addProvider(BC);
    }
    #End If
   
    #End if
   
    #If B4A or B4J
   
   Dim su As StringUtils
   Dim privkey() As Byte
   privkey = convertOpenSSL2b4x(PrivateKey)
   Dim kpg As KeyPairGenerator
   kpg.Initialize("RSA", 1024)
   kpg.PrivateKeyFromBytes(privkey)
   Dim Enc As Cipher
   Enc.Initialize("RSA/ECB/PKCS1Padding")
   Dim bc As ByteConverter
   Return bc.StringFromBytes(Enc.Decrypt(su.DecodeBase64(encryptedstring),kpg.PrivateKey,False),"UTF8")
       
        #End If
       
End Sub

Public Sub convertOpenSSL2b4x(pstrOpenSslRsaKey As String) As Byte()
   Dim su As StringUtils
   Dim bc As ByteConverter
   
   Dim strOpenSslKey As String
   Dim bytOpenSslKey() As Byte
   
   Dim bytB4xKey() As Byte

   bytOpenSslKey = su.DecodeBase64(pstrOpenSslRsaKey)
   strOpenSslKey = bc.StringFromBytes(bytOpenSslKey, "UTF8")
   
   ' in case it's a public key
   strOpenSslKey = strOpenSslKey.Replace("-----BEGIN PUBLIC KEY-----", "")
   strOpenSslKey = strOpenSslKey.Replace("-----END PUBLIC KEY-----", "")
   
   ' in case it's a private key in PKCS#1
   strOpenSslKey = strOpenSslKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
   strOpenSslKey = strOpenSslKey.Replace("-----END RSA PRIVATE KEY-----", "")
   
   ' in case it's a private key in PKCS#8
   strOpenSslKey = strOpenSslKey.Replace("-----BEGIN PRIVATE KEY-----", "")
   strOpenSslKey = strOpenSslKey.Replace("-----END PRIVATE KEY-----", "")
   
   bytB4xKey = su.DecodeBase64(strOpenSslKey)
   
   Return bytB4xKey
End Sub

I would be glad I you could check whether this code runs on B4i and B4j since you aim for a B4X solution. Thank you very much!

Thanks,
LerneBasic4Android
 

KMatle

Expert
Licensed User
Longtime User
The code runs on B4A and B4J (B4i -> don't know). I use it in all of my projects :)

But after digging deeper it turned out to be a simple conversion problem explained by KMatle in this post

It's simple: The encryption lib uses the pub and priv key without headers and without crlf so you've to add/remove it depending on which instance (B4x vs. OpenSSL) uses it. As you've pointed just copy my code. I was too lazy to parse the keys (APN1 format) for start/end of the data. So I checked how a 2048, 4096 and 8192 key looks like and grabbed the data :)

Generate keys in OpenSSL via console:

Create a *.bat file and add these lines. I use XAMPP which comes with Apache and OpenSSL. Change the path.
It writes 2 files: Private and Public Key. Format is the same you've used:

B4X:
cd C:\xampp\apache\bin
openssl genrsa  -out PrivateKey.pem 4096
openssl pkcs8 -topk8 -inform pem -in PrivateKey.pem -outform pem -nocrypt -out C:/xampp/htdocs/licenceadmin/PrivateKey.pem
openssl rsa -pubout -in PrivateKey.pem -out C:/xampp/htdocs/licenceadmin/PublicKey.pem
pause

This format can be used (with or without removing the headers and crlf) in B4x, PHP, .net and all other platforms (even on the ESP32)

In my apps I create the keys by code and publish the pubkey to e.g. my webserver and store it there for all the clients in a db. I create 2 formats (the non header/non crlf) and the one with.
 
Top