3

I have these methods from Java that I need to implement in Swift:

    fun encryptMessage(content: String): String {
        val cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING")
        cipher.init(Cipher.ENCRYPT_MODE, publicKey)
        val encryptedBytes = cipher.doFinal(content.toByteArray())
        val encryptedData = Base64.encodeToString(encryptedBytes, Base64.DEFAULT)
        return ENCRYPT_MARK + encryptedData
    }
​
    fun decryptMessage(content: String): String {
        val cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING")
        cipher.init(Cipher.DECRYPT_MODE, privateKey)
        var encryptedData = Base64.decode(content.substring(ENCRYPT_MARK.length), Base64.DEFAULT)
        val decryptedBytes = cipher.doFinal(encryptedData)
        return String(decryptedBytes)
    }

I am not very good in encryption and decryption. I tried several libraries and codes I found but nothing worked for me.

Example string that I need to decrypt:

"p+KuB7UVUteY8g3HVMxo+7h2xhZQxhPjPayMolqq8EYyIknbQjgqHjC95NR8/le6G0F8SQACrOdj\nTlPU1+o7bZrE8ukI7B35i9sWZns3Y2scA9U7yz8z5KsTZ240+4+Xd7dZwfQ49Z6J7nNOfBR/97pq\n2l1QKe0/SThUEraZyts=\n"

My private key:

"-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQDjOUZOZgIYWDMkyhyNb/gsFUDbDB3a+FWwT0d2HE7cd3bqebYLg\nKAMqwpc8nCHR77kO23Nd/U5WKVWwFcHUQVtkMcX4QNDtxSV72LRNaSaQZkwBofw9O\nu338mm1hR0DEYzYPXKuyaP3l5/pvZyvOHrA+i0ZD7pUohEtiDsuNhZbwIDAQABAoG\nBAJoM6q2cWy9GHOaEYHdDwm2guyfHPzaFxxKRrVFWP+EY3XZ6rgF+YwQzsgLyG3ic\nG7+AyyDSg18tymrWXCqJs7Mdrxq3xZmdCzwTcfgxZcwiFG0caK/jbA8rXO60xecag\nZRR+AyWa+2wnwt5xPtFcqk7GhqkWIolzQddW7L3CIuBAkEA9Xw+fnLrbR3WaxTpa3\n88NEEgvyxIS2eGc+lRUbC8w2xX5qDtV8Ak7rxkmXGURJ6tvwyUi/Q+5y+X20mBtt8\nsKwJBAOz0yph9n4iHtC+BL5U+LfpZuUO6uUctbmfXs+fU2glI8rAwXhanBCs0Ph/0\nG7aXmNKHvcSjQ9N5mhTPBvhpcc0CQH4PPTBF5ytzZQY8CNmQzuOuhhhlrwI5uUuQh\npfCgEyCOGlQPlEPdGe8CpTZRGAwc9xlo2pzFFI3mG2dQ6Ua2V0CQQCSMY11e7wbzi\n37SScEWzKezRCimueI5JzDcK/MjuRe6iThU1YZf73wsfDKYh9fDjT5X0pTsa89ID9\nSK1DPnq7ZAkB6ybvpomovyBBgSjjTEwwSHyAIr5HIE2hkDjer6/87/WNKEw1yg11b\ngSoJC67f1xnLj7bv9/EJRPWTokCaRRm/\n-----END RSA PRIVATE KEY-----"

1. SecKeyCreateDecryptedData

let encryptedMessageData = encryptedMessage.data(using: .utf8)
if let decryptedMessage:Data = SecKeyCreateDecryptedData(privateSecKey!, .rsaEncryptionPKCS1, encryptedMessageData as CFData, error) as Data? {
    print("We have an decrypted message \(String.init(data: decryptedMessage, encoding: .utf8)!)")
} else {
    print("Error decrypting")
}
  • not working. I get print "Error decrypting" but no error in variable

2. SwiftyRSA

    let encrypted = try EncryptedMessage(data: key.data(using: .utf8)!) // try EncryptedMessage(base64Encoded: key)
    
    let privateKey = try PrivateKey(pemEncoded: keyPair.privateKey)
    let decryptedKey = try encrypted.decrypted(with: privateKey, padding: SecPadding.PKCS1)
  • getting chunkDecryptFailed(index: 0) error.

3. CryptorRSA

    let data = base64.data(using: .utf8)!
    let encryptedData = try CryptorRSA.createEncrypted(with: data)
    let decryptedData = try encryptedData.decrypted(with: privateKey, algorithm: .sha1)
  • getting error:

    Error Domain=NSOSStatusErrorDomain Code=-50 "RSAdecrypt wrong input (err -27)" UserInfo={numberOfErrorsDeep=0, NSDescription=RSAdecrypt wrong input (err -27)}

I even try to implement decrypt with these lines of code but without luck:

static func decrypt(string: String, privateKey: String?) -> String? {
    guard let privateKey = privateKey else { return nil }
    
    let keyString = privateKey.replacingOccurrences(of: "-----BEGIN RSA PRIVATE KEY-----\n", with: "").replacingOccurrences(of: "\n-----END RSA PRIVATE KEY-----", with: "")
    guard let data = Data(base64Encoded: keyString, options: .ignoreUnknownCharacters) else { return nil }
    
    var attributes: CFDictionary {
        return [kSecAttrKeyType         : kSecAttrKeyTypeRSA,
                kSecAttrKeyClass        : kSecAttrKeyClassPrivate,
                kSecAttrKeySizeInBits   : 2048,
                kSecReturnPersistentRef : true] as CFDictionary
    }
    
    var error: Unmanaged<CFError>? = nil
    guard let secKey = SecKeyCreateWithData(data as CFData, attributes, &error) else {
        print(error.debugDescription)
        return nil
    }
    return decrypt(string: string, privateKey: secKey)
}

static func decrypt(string: String, privateKey: SecKey) -> String?  {
    let buffer = [UInt8](string.utf8)
    
    let keySize = SecKeyGetBlockSize(privateKey)
    var messageDecrypted = [UInt8](repeating: 0, count: keySize)
    var messageDecryptedSize = keySize
    
    var status = SecKeyDecrypt(privateKey, SecPadding.PKCS1, buffer, buffer.count, &messageDecrypted, &messageDecryptedSize)
    
    if status != noErr {
        print("Decryption Error!")
        return nil
    }
    
    let result = String(bytes: messageDecrypted, encoding: .utf8)
    print(result)
    return result
}

What am I doing wrong? How can I decrypt that example data? Thanks for help

Libor Zapletal
  • 13,752
  • 20
  • 95
  • 182

2 Answers2

1

Below you'll see a quick example of encryption and decryption using a randomly generated key. I've added bang operators for simplicity, but it might just show you how you can encrypt and decrypt data using RSA in Swift.

let attributes = [
    kSecAttrKeyType: kSecAttrKeyTypeRSA,
    kSecAttrKeySizeInBits: 2048,
    kSecAttrKeyClass: kSecAttrKeyClassPrivate
] as CFDictionary

var error: Unmanaged<CFError>?
let privateKey = SecKeyCreateRandomKey(attributes, &error)!
let publicKey = SecKeyCopyPublicKey(privateKey)!

let message = Data("Hello World".utf8)

let ciphertext = SecKeyCreateEncryptedData(publicKey, .rsaEncryptionPKCS1, message as CFData, &error)! as Data

print(ciphertext as NSData)

let plaintext = SecKeyCreateDecryptedData(privateKey, .rsaEncryptionPKCS1, ciphertext as CFData, &error)! as Data
print(String(data: plaintext, encoding: .utf8) ?? "Non UTF8")

Now, we can do the same thing using your key. Your key currently is in PEM format. You'll have to remove the header, footer and all newlines to convert it to DER, iOS is quite strict in what is possible. You can replace some of the lines above with below code.

let keyBytes = Data(base64Encoded: "<your DER encoded bytes>")! as CFData

var error: Unmanaged<CFError>?
let privateKey = SecKeyCreateWithData(keyBytes, attributes, &error)!
let publicKey = SecKeyCopyPublicKey(privateKey)!

So that leaves me just to tell you that your cipher text is messed up or the cipher text was created with a different public key than the public key that belongs to your private key. I'm guessing the first, as I see new lines in a base64 string...

Now, some important notes:

  • Do not ship your product with the private key you posted here. It's called a privatekey for a reason
  • RSA is getting outdated, which is especially visible in its key sizes. You should not use 1024 bits RSA keys, they're not safe. Switch to keys of at least 2048 bits
  • Encryption is done using the publickey, decryption is done using the privatekey (as shown in the example)
  • Encryption using RSA keys is limited; only small amounts of data can be encrypted or decrypted and RSA is slow. If you need to encrypt lots of data and need it to be as fast as possible, you might want to check out AES or ChaCha (though both of them are symmetric algorithms)

P.S. Your DER encoded string is:

MIICXQIBAAKBgQDjOUZOZgIYWDMkyhyNb/gsFUDbDB3a+FWwT0d2HE7cd3bqebYLgKAMqwpc8nCHR77kO23Nd/U5WKVWwFcHUQVtkMcX4QNDtxSV72LRNaSaQZkwBofw9Ou338mm1hR0DEYzYPXKuyaP3l5/pvZyvOHrA+i0ZD7pUohEtiDsuNhZbwIDAQABAoGBAJoM6q2cWy9GHOaEYHdDwm2guyfHPzaFxxKRrVFWP+EY3XZ6rgF+YwQzsgLyG3icG7+AyyDSg18tymrWXCqJs7Mdrxq3xZmdCzwTcfgxZcwiFG0caK/jbA8rXO60xecagZRR+AyWa+2wnwt5xPtFcqk7GhqkWIolzQddW7L3CIuBAkEA9Xw+fnLrbR3WaxTpa388NEEgvyxIS2eGc+lRUbC8w2xX5qDtV8Ak7rxkmXGURJ6tvwyUi/Q+5y+X20mBtt8sKwJBAOz0yph9n4iHtC+BL5U+LfpZuUO6uUctbmfXs+fU2glI8rAwXhanBCs0Ph/0G7aXmNKHvcSjQ9N5mhTPBvhpcc0CQH4PPTBF5ytzZQY8CNmQzuOuhhhlrwI5uUuQhpfCgEyCOGlQPlEPdGe8CpTZRGAwc9xlo2pzFFI3mG2dQ6Ua2V0CQQCSMY11e7wbzi37SScEWzKezRCimueI5JzDcK/MjuRe6iThU1YZf73wsfDKYh9fDjT5X0pTsa89ID9SK1DPnq7ZAkB6ybvpomovyBBgSjjTEwwSHyAIr5HIE2hkDjer6/87/WNKEw1yg11bgSoJC67f1xnLj7bv9/EJRPWTokCaRRm/"
Bram
  • 2,718
  • 1
  • 22
  • 43
  • Thanks for very descriptive answer. Problem is that all data is set and I am trying to implement solution that works for Android. So cipher text should be correct. I have to be able to decrypt it. Is it possible that I should remove new lines from that string? Or is there another way how to "fix" cipher text to be correct/to be working with private key? – Libor Zapletal May 19 '21 at 07:25
  • The cipher text is correct when you remove the newline characters (128 bytes of data is correct for a 1024bits RSA key). What you can try to do is create encrypted data on iOS and try to decrypt it on Android and see if that works. You could try to decrypt that "Hello World" example on Android – Bram May 19 '21 at 11:38
0

Sample Hurdle

Maybe it's this problem (just align it for your private key):

The generated RSA-Keys from Android & iOS look pretty much the same AND they carry basically the same information.

Still ... .. .

  • Java code produces RSA keys in SPKI-Format.
  • Swift assumes PKCS#1-formatted RSA keys.

. .. ... Base64 encoded you might want to analyze your keys with:

Sample Solution

You could use the BouncyCastle Java-dependency to convert your key before sending it to iOS.

d.braun1991
  • 151
  • 1
  • 13