I have Delphi client/software suite that uses RSA 1024 private and public keys in the following protocol (client has public key, server has private key):

  1. client generates string A and encrypts it with the public key as B=encrypt(A, public key) and sends B to the server;
  2. server decrypts B with the private key and modifies it a bit and encrypts the result with private key and sends result C to the client: C=encrypt( modification( decrypt(B, private key)), private key)
  3. client receives C and decrypts it with the public key and gets D: D=decrypt(C, public key)

I am trying to migrate partially this process to Android Kotlin so that the server side remains Delphi (with TurboPower LockBox 2 ciphers) and the client side is implemented in Andoird Kotlin with javax.crypto/BouncyCastle.

I have managed to implement step 1 and step 2, but I have stuck in the implementation of step 3 on Android Kotlin. My code is:

val publicKeyString = "MIGIAoGBALtEMVXxHBWzBx/AzO/aOHrYEQZB3VlqYBvqX/SHES7ehERXaCbUO5aEwyZcDrdh2dMTy/abNDaFJK4bEqghpC6yvCNvnTqjAz+bsD9UqS0w5CUh3KHwqhPv+HFGcF7rAuU9uoJcWXbTC9tUBEG7rdmdmMatIgL1Y4ebOACQHn1xAgIlKg=="
val registerStringFromService = "HWve0ZI8vxVT3jUq2K18swiHmUMWz2trbT3/7bCnng0jSxkfvOYQXPfPyBW+1d+F39NkmwFHJaUNFw7zRkcHzT7XZltzXQHNfLA/DtXW6IcYd8UFhPLEMOW26OaOM3yK71fYyk0YoriT33m+/W3iZq2NLnnhW6vtBkJXkI9esT8="

    val publicKeyBytes: ByteArray = Base64.decode(publicKeyString, Base64.DEFAULT)
    val seq: DLSequence = ASN1Sequence.fromByteArray(publicKeyBytes) as DLSequence
    val modulus: ASN1Integer = seq.elementAt(0) as ASN1Integer
    val pubExp: ASN1Integer = seq.elementAt(1) as ASN1Integer
    val publicKeySpec = RSAPublicKeySpec(modulus.positiveValue, pubExp.positiveValue)
    val kf: KeyFactory = KeyFactory.getInstance("RSA")
    val publicKey: PublicKey = kf.generatePublic(publicKeySpec)

    //val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
    //val cipher: Cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding")
    val cipher: Cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
    cipher.init(Cipher.DECRYPT_MODE, publicKey)
    val registerBytesFromService = Base64.decode(registerStringFromService, Base64.DEFAULT)
    val decryptedRegisterBytes = cipher.doFinal(registerBytesFromService)
    val decryptedRegisterString : String = Base64.encodeToString(decryptedRegisterBytes, Base64.DEFAULT)

I am trying all 3 transformations for the Cipher.getInstance(...) and the error messages are correspondingly:

//Cipher.getInstance("RSA/ECB/PKCS1Padding")
javax.crypto.BadPaddingException: error:0400006b:RSA routines:OPENSSL_internal:BLOCK_TYPE_IS_NOT_01
    at com.android.org.conscrypt.NativeCrypto.RSA_public_decrypt(Native Method)
    at com.android.org.conscrypt.OpenSSLCipherRSA$DirectRSA.doCryptoOperation(OpenSSLCipherRSA.java:407)
    at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:316)
    at javax.crypto.Cipher.doFinal(Cipher.java:2055)

//Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding")
java.security.InvalidKeyException: Only private keys may be used to decrypt
    at com.android.org.conscrypt.OpenSSLCipherRSA$OAEP.engineInitInternal(OpenSSLCipherRSA.java:537)

//Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
java.security.InvalidKeyException: Only private keys may be used to decrypt
    at com.android.org.conscrypt.OpenSSLCipherRSA$OAEP.engineInitInternal(OpenSSLCipherRSA.java:537)
    at com.android.org.conscrypt.OpenSSLCipherRSA.engineInit(OpenSSLCipherRSA.java:245)
    at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
    at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)

So - I am not sure whether the decryption with the public key is allowed or not. The error messages are confusing and give impression that decryption with the public key is allowed with one specific transformation (Cipher.getInstance("RSA/ECB/PKCS1Padding")) and is not allowed with the remaining 2 transformations. But I am not sure about this, therefore I am asking this?

My protocol seems the be very nice and uses one key-pair only? Standard scenario presumes that public key is used for the encryption and the private key is used for the decryption and if public key is not allowed for the decryption, then I should modify my protocol to use 2 key pairs. It can be done, but it requires modification of the server software as well.

I would be nice to stick with the protocol and to get my decryption code working.

Additional Info 1: I tried to use:

val cipher: Cipher = Cipher.getInstance("RSA", "BC")
//or
val cipher: Cipher = Cipher.getInstance("RSA")

And then the more specific error message were - for different key pairs:

java.lang.IllegalArgumentException: RSA publicExponent is even
    at com.android.org.bouncycastle.crypto.params.RSAKeyParameters.<init>(RSAKeyParameters.java:28)
    at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.RSAUtil.generatePublicKeyParameter(RSAUtil.java:48)
    at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:293)
    at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:411)
    at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
    at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
    at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
    at javax.crypto.Cipher.init(Cipher.java:1143)
    at javax.crypto.Cipher.init(Cipher.java:1084)

javax.crypto.BadPaddingException: error:03000068:bignum routines:OPENSSL_internal:CALLED_WITH_EVEN_MODULUS
    at com.android.org.conscrypt.NativeCrypto.RSA_public_encrypt(Native Method)
    at com.android.org.conscrypt.OpenSSLCipherRSA$DirectRSA.doCryptoOperation(OpenSSLCipherRSA.java:398)
    at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:316)
    at javax.crypto.Cipher.doFinal(Cipher.java:2055)

java.lang.IllegalArgumentException: RSA modulus has a small prime factor
    at com.android.org.bouncycastle.crypto.params.RSAKeyParameters.validate(RSAKeyParameters.java:50)
    at com.android.org.bouncycastle.crypto.params.RSAKeyParameters.<init>(RSAKeyParameters.java:32)
    at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.RSAUtil.generatePublicKeyParameter(RSAUtil.java:48)
    at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:293)
    at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:411)
    at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
    at javax.crypto.Cipher.tryCombinations(Cipher.java:2877)
    at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
    at javax.crypto.Cipher.init(Cipher.java:1143)

Of course, I tried to regenerate the key pair, but I have not managed to came up with the pair, that suites. I used Delphi TurboPower LockBox2 generation and apparently - they have no validation.

I tried to use https://www.csfieldguide.org.nz/en/interactives/rsa-key-generator/ for alternative key-pair generation - I used parameters 1024 bits and PKCS#1 (base 64) which have me the public key in the expected format, but the private key was over 800 characters long and it was in contradiction which I have in Delphi program - I have length 356 for Delphi generated private keys - more than 2 times smaller.

So, nothing works. Seems like physics...

0

There are 0 best solutions below