Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RSA Encryption Java/Kotlin

I been trying to encrypt a simple string in Kotlin/Java with a premade public key but I've had no success. This is what I'm currently doing and commented is what I've currently tried.

val toEncrypt = "8uUrfe4OcJVUT5lkAP07WKrlGhIlAAwTRwAksBztVaa0hHdZp50EFjOmhrAmFsLQ"
val publicKeyRaw =
        "-----BEGIN PUBLIC KEY-----\n" +
                "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJ\n" +
                "hCm2Hjl00QGkxeO12Py+jytTNYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDg\n" +
                "GvBhlwrWQXfI9LdA2M3xbr/4wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYY\n" +
                "yGK2Fsnu67FKWjgVvQIDAQAB\n" +
                "-----END PUBLIC KEY-----"

val reader = PemReader(StringReader(publicKeyRaw))
val pemObject = reader.readPemObject()
val keyBytes: ByteArray = pemObject.content
val keySpec: EncodedKeySpec = X509EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(keySpec)
val cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.ENCRYPT_MODE, key)
val cipherData: ByteArray = cipher.doFinal(toEncrypt.toByteArray())

val encryptedData =  Base64.encodeToString(cipherData, Base64.DEFAULT)

Log.e("TAG", "encryptedData: $encryptedData")

Here is the code I've already tried:

/*
    val publicKey = publicKeyRaw.replace("\n", "")
        .replace("\\n", "")
        .replace("-----BEGIN PUBLIC KEY-----", "")
        .replace("-----END PUBLIC KEY-----", "")
   */

    /*
    val pemParser = PEMParser(StringReader(publicKeyRaw))
    val pemKeyPair : PEMKeyPair = pemParser.readObject() as PEMKeyPair
    val key = JcaPEMKeyConverter().getPublicKey(pemKeyPair.publicKeyInfo)
    */

/*
   val keyFactory = KeyFactory.getInstance("RSA")
   val keyBytes: ByteArray = Base64.decode(publicKey.toByteArray(), Base64.DEFAULT)
   val spec = X509EncodedKeySpec(keyBytes)
   val fileGeneratedPublicKey = keyFactory.generatePublic(spec)
   val rsaPub: RSAPublicKey = fileGeneratedPublicKey as RSAPublicKey
   val publicKeyModulus: BigInteger = rsaPub.modulus
   val publicKeyExponent: BigInteger = rsaPub.publicExponent


   val keyFactoryAlt = KeyFactory.getInstance("RSA")
   val pubKeySpec = RSAPublicKeySpec(publicKeyModulus, publicKeyExponent)
   val key = keyFactoryAlt.generatePublic(pubKeySpec) as RSAPublicKey
*/

/*
    val reader = PemReader(StringReader(publicKeyRaw))
    val pemObject = reader.readPemObject()
    val keyBytes: ByteArray = pemObject.content
    val keySpec: EncodedKeySpec = X509EncodedKeySpec(keyBytes)
    val keyFactory = KeyFactory.getInstance("RSA")
    val key = keyFactory.generatePublic(keySpec)
    */
/*
    val keyFactory = KeyFactory.getInstance("RSA")
    val keyBytes: ByteArray = Base64.decode(publicKey.toByteArray(), Base64.DEFAULT)
    val spec = X509EncodedKeySpec(keyBytes)
    val fileGeneratedPublicKey = keyFactory.generatePublic(spec)
    val rsaPub: RSAPublicKey = fileGeneratedPublicKey as RSAPublicKey
    val publicKeyModulus: BigInteger = rsaPub.modulus
    val publicKeyExponent: BigInteger = rsaPub.publicExponent
    */

/*
val pemParser = PEMParser(StringReader(publicKey))
val pemKeyPair : PEMKeyPair = pemParser.readObject() as PEMKeyPair
val encoded : ByteArray = pemKeyPair.publicKeyInfo.encoded
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(PKCS8EncodedKeySpec(encoded))
 */

and it actually generates a String but when using tools like: https://8gwifi.org/rsafunctions.jsp it shows an error that it's not valid, even tough I generated the key there with a 1024 key size

Error

My question is: How to cypher with that kind of key in Java/Kotlin. (you may generate that kind of key on any site you like or the site provided)

here is a pair I used:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJ
hCm2Hjl00QGkxeO12Py+jytTNYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDg
GvBhlwrWQXfI9LdA2M3xbr/4wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYY
yGK2Fsnu67FKWjgVvQIDAQAB
-----END PUBLIC KEY-----



-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJhCm2Hjl00QGkxeO12Py+jytT
NYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDgGvBhlwrWQXfI9LdA2M3xbr/4
wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYYyGK2Fsnu67FKWjgVvQIDAQAB
AoGActBq8wmTSiVh7s7f4d6d+D6ACZscrHjwsBtcuwUAIOONgO8TtASBNNmSjgsG
kTm/TuvEVfdMjd2rZE0UE/wE+2BOoHTlkVjcKMxoM8KbP/4RBDYlYmWTseiS8zmp
dGwchOzsoWKnhZtnvMrki0f1SdMq4J6g9RncFIrUSKWJ1MECQQDp0s4v+sKo423X
2YSAhB8j1LMPoRlioXSmvVrHGINzGoHt2tRvGqqHaHbd/9QkkhpfeeBcdrv/xOaH
fVH08dnJAkEAjIgFRe6QEDNvm1qCRx6ata047N188MxdHgKHwQBsv48dxqljQrFS
N1yEfXsv6PjLk3DCD8Wi3FTOgftpZVeWVQJBAIpc+TABJkGEW1KYX8Ug6cBtNAxy
my/3NK0abeZUxixNqkcS8BRS5kg8c+KIaYO+hSasWyy8AiGm5XeVm/LjTqkCQEGQ
dGVcF/p3BOsGHyHvNV7tolFgRJpTvl3x8EQrXpFAxDObc6P59tG9aFLi1kdrTA9N
3DxfiMwjBPW/xjxx0MECQBtaSSfTNUYBP64+evjY4HaV9GI5AK83webyF73axXIq
4dyadIdIo78Yaz+f2myX7vyfUlU5iM8QuPMN2KCM3CE=
-----END RSA PRIVATE KEY-----

here is the code I used: https://github.com/Raykud/TestEncryption

Edit: This is the generated encrypted text.

NO_WRAP: c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQxMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xKqbovZP8eoZrvp2azFOk=

DEFAULT: 
c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQ
xMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xK
qbovZP8eoZrvp2azFOk=
like image 896
Raykud Avatar asked Oct 23 '25 23:10

Raykud


1 Answers

The cause of your problem is that different paddings are used.

The posted ciphertext can be reproduced (with the posted public key) or decrypted (with the posted private key) if no padding is applied (RSA/ECB/NoPadding, see here). This RSA variant is called textbook RSA and shouldn't be used in practice because it's insecure. The website applies PKCS#1 v1.5 padding (the first three options) or OAEP (the last three options), the insecure textbook RSA is not supported at all. I.e. the paddings are incompatible and decryption therefore fails.

There are two ways to specify the encryption with Cipher#getInstance, the full variant algorithm/mode/padding or the short variant algorithm, see here. In the latter, mode and padding are determined by provider-specific default values. And because they are provider specific, they can be different in different environments, which can lead to cross-platform problems, as in this case. That is why the full variant should always be used!

Cipher#getInstance("RSA") obviously applies textbook RSA in your environment, i.e. no padding. I can reproduce this behavior e.g. in Android Studio (API level 28). In contrast, in Eclipse (Kotlin plugin 0.8.14) PKCS#1 v1.5 padding is used.

So the solution to the problem is to explicitly specify the padding according to the environment used, e.g. for PKCS#1 v1.5 padding usually with RSA/ECB/PKCS1Padding or RSA/NONE/PKCS1Padding, see here. Note that the scheme algorithm/mode/padding is used for both symmetric and asymmetric encryption. While the mode of operation is defined for symmetric encryption, it's generally not defined for asymmetric encryptionsuch as RSA, i.e. ECB has no meaning in the context of RSA, but is still used by some providers on the specification.

Another possible problem is that the website can't handle line breaks, but it doesn't remove them automatically, so decryption fails if the ciphertext contains line breaks. The option Base64.DEFAULT generates line breaks after 76 characters. These must therefore be removed (e.g. manually) before the ciphertext is decrypted using the website. Alternatively, Base64.NO_WRAP can be used, which produces the ciphertext on a single line.

like image 181
Topaco Avatar answered Oct 25 '25 14:10

Topaco