My iOS app needs to decrypt a string that was encrypted on a Java backend. The backend encrypts the string using these parameters: Cipher.getInstance("RSA/ECB/PKCS1Padding").
As a simple proof of concept, using OpenSSL from the Terminal I extracted the private key from the pfx certificate that they sent me:
$ openssl pkcs12 -in certificate.pfx -nocerts -out key.pem -nodes
$ openssl rsa -in key.pem -out server.key
Then, I hardcoded the private key as a String in my code and tried to decrypt the encrypted message using the SwiftyRSA lib:
import UIKit
import SwiftyRSA
class ViewController: UIViewController {
private let rsaPemPrivateKey = """
-----BEGIN RSA PRIVATE KEY-----
THE ACTUAL PRIVATE KEY...
-----END RSA PRIVATE KEY-----
"""
private let encryptedString = "ciSvTLKRAMqz8d8SEX8epvkyyrfAoiX28Sd1RhYbsz7oOg3/taXZxZkTeCbVeFXB8Mf8eWn2SGMcVIrmFGuxqyRoC5SluJDgrup0wcG/XRg7lrt7oD6esVBAGZT5nmS79mUoAC4CKW5cHclCh66JUysATSIvI9qrrXNUnBApGJc="
override func viewDidLoad() {
super.viewDidLoad()
do {
try decryptWithPrivateKey()
} catch {
print("Exception:")
print(error)
}
}
private func decryptWithPrivateKey() throws {
let privateKey = try PrivateKey(pemEncoded: rsaPemPrivateKey)
let encrypted = try EncryptedMessage(base64Encoded: encryptedString)
let clear = try encrypted.decrypted(with: privateKey, padding: SecPadding.PKCS1)
print("Decrypted string is: \(try clear.string(encoding: .utf8))")
}
}
This results in the following error, when the method encrypted.decrypted is called:
Exception:
chunkDecryptFailed(index: 0)
I also tried with other libraries, but similar errors occurred.
Here's the result with the SwiftRSA lib:
Exception:
decryptionFailed(error: Optional(Error Domain=NSOSStatusErrorDomain Code=-50 "RSAdecrypt wrong input (err -1)" UserInfo={NSDescription=RSAdecrypt wrong input (err -1)}))
And here's the result with the SwCrypt lib:
Exception:
decodeError
I'm not sure what could be wrong here. At first I thought the problem might be with the private key, but none of the libraries throw an exception when I instantiate their corresponding PrivateKey objects with it. Perhaps the problem is with the encrypted string I'm trying to decrypt, but it seems that no base64 decoding is required, as the EncryptedMessage object expects a base 64 encoded string anyway.
Is there anything else I can try to decrypt this RSA/ECB/PKCS1Padding encrypted string in Swift? (although in Java they pass ECB as the mode of operation, it looks like it don't actually use ECB under the hood, so I don't think I need to implement any custom management of the block size of the encrypted string as they say here).
-- Edit --
Here's a few other details as requested by zaitsman:
Dummy certificate's private key:
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAt+733lAMe2uhg3pLdhv///wuwYtlJNiRbC3xfz6y/9qkXoDn
3yKleJtw1JI7e1uZIN1h/bY4DUezSDuqio7VpHZ8VpnQ3pm9U87Fozpaqi/62hqM
MzQ7B7lWsrSUrcjgEDZAWehWLdMEZiRlgQL3IeUT8bh4YMMXL9a+3gf+dUHLge5J
FIVJ/3VjyPbRn/05KxPlUwxtkP5TIAMWsJkNbO38i+ymQ9GFhBuaV2rOC2WOV3eo
VMd7LqfIBhPdA/CDpYuo/ZxVPdY2OOdXTPX1JY2qKzoE4sqUrlwzuYaqLbGR6nU+
ZSMUVtivZN+fTCWS6HcmNvEmB14I5DTWJ5BjaQIDAQABAoIBABKS5SDkAH6uHb9D
KD+jEDTvaGFPDEWuQPElqo9o63Z+w75vUfrsar1FJR2yLqOEWnBBKtYOApcEuIwA
ynX3eoeDF4c/PSJdcAr4hGi5SdKJydEggSviiFt9Uc32AlWSRV4dvF3X4mv6NSWD
Y2SlwgMAOQVd1Xof+UVxcBDHyeBZOmFIVkvs+JKDKLAA+Gw6uCmQ+m2kUUtEAh7G
yPQdbqwYdi6vnyVOTRfoyC6J/fmRh0P3ZJtWEctGmlkGVVDt8qYHDwKrtACsbsXh
2dRkVOIUM4JclCK9vh/1xFpn9m+yaAI8c8XXLuN9ZyglDM9nmF6iPrf1n7DRDVRU
lRqO6okCgYEAv0+lTquO1S0RbCSzGCkKJ59JP12PknJQHC9zM6Psa0gd1LN3LYQ3
byEjCmJJhhAnBw95PuTF/B0ESa7nhSvAUazipc123gdTx/7V8raE0WlKZM89X6Vy
H4ZjvNikr/2S2WaNZWwPYNC5b2raOGZP5sEUpGBQz6M9UbZz42SZsN8CgYEA9iCy
DkhX1tzsKQhjRdXU9V/N5daXH65fto+qlpQMOcwIKDAe6DH36lugSR4gaQh+KtmR
nWX/58TM0iQWDIUMs3sbyBeESfCh3LEH42EN1M62BNC6F7A8pnLw8qfGeu+kfu5X
fAvqv0HgBJP4Duqdl30Ux9h4RqtGTyWiQaEnjLcCgYBDB3LR14YZ5sp963XcdzSZ
oVOWof9VvjuXRIDGjR6ekAvzpFWTWwnZI/EVCE7ea/ZgoOlIQfukU75W2rM/k460
jzByC0UkkcinAACSI6TxgkSQgZPtWRhdN0bmubkp+FxlU2sjJ/NTZo4yHWwL6r0A
CpJi7WQi+/zWDmkYOP0oXQKBgQDdC1weK6aH131ZFKljjLlnbZVTN7gdwdM/+CLy
fR/FwJIK1bzMOCQ5G/UF9cKR0gjNnvyB6Zs8oah5ieMrd0qC3quCtmweo7gapfs5
oG51kvgxtuuKXsL5kf0HUNqWiianwJJdW66F+jmgouuDKf5CkRlaqfTLMwNADcJ9
QqhsXQKBgQCJ4hUy3ptM6KUDG0oAT9s+cojVAmsCm5zITHkaFfkjP0W8srGXwrWx
63wLQCy84GOCFH3jecHSKHapq9V0laL+ARnepj6naO8ivMQFSucyAOeX7CfJAc45
BoezzHAqR1cnGn52G57ycqrX7eCHSULfIcYU7VwcqKJzxf1w+A+AVw==
-----END RSA PRIVATE KEY-----
Java code used to encrypt (the minimal reproducible example):
I asked the backend team for an example of the code they use to encript, and they sent me this Kotlin function:
fun String.encryptWithPublicKey(publicKey: String): String {
try {
val base64Decoder = Base64.getDecoder()
val decoded = base64Decoder.decode(publicKey.toByteArray(Charsets.UTF_8))
val key = RSAPublicKey.getInstance(decoded)
val keySpec = RSAPublicKeySpec(key.modulus, key.publicExponent)
val rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec)
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, rsaKey)
val encrypted = cipher.doFinal(this.toByteArray(Charsets.UTF_8))
return ByteString.of(encrypted, 0, encrypted.size).base64()
} catch (e: Exception) {
throw e
}
}
Sample encrypted string, b64 encoded:
Here's the base 64 encoded, encrypted string I get from the backend response:
ciSvTLKRAMqz8d8SEX8epvkyyrfAoiX28Sd1RhYbsz7oOg3/taXZxZkTeCbVeFXB8Mf8eWn2SGMcVIrmFGuxqyRoC5SluJDgrup0wcG/XRg7lrt7oD6esVBAGZT5nmS79mUoAC4CKW5cHclCh66JUysATSIvI9qrrXNUnBApGJc=
Sample raw value:
I don't have the exact value right now, but it should be a numeric value similar to this:
1234 5678 9012 3456