3

I need to decrypt data with a RSA public key on macOS, by googling I know we can use method SecKeyCreateDecryptedData of Security.framework to achieve that, but it leads to two problems:

  1. SecKeyCreateDecryptedData accepts a private key to execute decryption, but in my situation, the data is encrypted with private key in the server-end, and needs to be decrypted with public key in the client-end.

  2. I tried to create SecKey from a RSA public key string, but failed.

My code:

import Foundation
 
func getPublicKey(from data: Data) throws -> SecKey {
    var error: Unmanaged<CFError>? = nil
    let publicKeyMaybe = SecKeyCreateWithData(
        data as NSData,
        [
            kSecAttrKeyType: kSecAttrKeyTypeRSA,
            kSecAttrKeyClass: kSecAttrKeyClassPublic
        ] as NSDictionary,
        &error)
    guard let publicKey = publicKeyMaybe else {
        throw error!.takeRetainedValue() as Error
    }
    return publicKey
}

func decrypt(key: SecKey, data cipherTextData: Data) -> Data? {
    let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM
    guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else {
        print("Can't decrypt. Algorithm not supported.")
        return nil
    }

    var error: Unmanaged<CFError>? = nil
    let clearTextData = SecKeyCreateDecryptedData(key,
                                                  algorithm,
                                                  cipherTextData as CFData,
                                                  &error) as Data?
    if let error = error {
        print("Can't decrypt. %@", (error.takeRetainedValue() as Error).localizedDescription)
        return nil
    }
    guard clearTextData != nil else {
        print("Can't decrypt. No resulting cleartextData.")
        return nil
    }
    print("Decrypted data.")
    return clearTextData
}

func testDecrypt() {
    let rawString = "0ed3a2c57f5dEJgqXT9760269b8cc5cd76f3afcf"
    let decodedData = Data.init(base64Encoded: rawString, options: [])!

    let pubKey = try! getPublicKey(from: kPubKey.data(using: .utf8)!) // Error: RSA public key creation from data failed
    let decryptedData = decrypt(key: pubKey, data: decodedData)!
    let decrypted = String.init(data: decryptedData, encoding: .utf8)!

    print(">>>>>>> decrypted string: \(decrypted)")
}

testDecrypt()

With method of Kazunori Takaishi, I tested all the algorithm, none of them is supported:

func decrypt(key: SecKey, data cipherTextData: Data) -> Data? {
        let algorithms: [SecKeyAlgorithm] = [
            .rsaSignatureRaw,
            .rsaSignatureDigestPKCS1v15Raw,
            .rsaSignatureDigestPKCS1v15SHA1,
            .rsaSignatureDigestPKCS1v15SHA224,
            .rsaSignatureDigestPKCS1v15SHA256,
            .rsaSignatureDigestPKCS1v15SHA384,
            .rsaSignatureDigestPKCS1v15SHA512,
            .rsaSignatureMessagePKCS1v15SHA1,
            .rsaSignatureMessagePKCS1v15SHA224,
            .rsaSignatureMessagePKCS1v15SHA256,
            .rsaSignatureMessagePKCS1v15SHA384,
            .rsaSignatureMessagePKCS1v15SHA512,
            .rsaSignatureDigestPSSSHA1,
            .rsaSignatureDigestPSSSHA224,
            .rsaSignatureDigestPSSSHA256,
            .rsaSignatureDigestPSSSHA384,
            .rsaSignatureDigestPSSSHA512,
            .rsaSignatureMessagePSSSHA1,
            .rsaSignatureMessagePSSSHA224,
            .rsaSignatureMessagePSSSHA256,
            .rsaSignatureMessagePSSSHA384,
            .rsaSignatureMessagePSSSHA512,
            .ecdsaSignatureRFC4754,
            .ecdsaSignatureDigestX962,
            .ecdsaSignatureDigestX962SHA1,
            .ecdsaSignatureDigestX962SHA224,
            .ecdsaSignatureDigestX962SHA256,
            .ecdsaSignatureDigestX962SHA384,
            .ecdsaSignatureDigestX962SHA512,
            .ecdsaSignatureMessageX962SHA1,
            .ecdsaSignatureMessageX962SHA224,
            .ecdsaSignatureMessageX962SHA256,
            .ecdsaSignatureMessageX962SHA384,
            .ecdsaSignatureMessageX962SHA512,
            .rsaEncryptionRaw,
            .rsaEncryptionPKCS1,
            .rsaEncryptionOAEPSHA1,
            .rsaEncryptionOAEPSHA224,
            .rsaEncryptionOAEPSHA256,
            .rsaEncryptionOAEPSHA384,
            .rsaEncryptionOAEPSHA512,
            .rsaEncryptionOAEPSHA1AESGCM,
            .rsaEncryptionOAEPSHA224AESGCM,
            .rsaEncryptionOAEPSHA256AESGCM,
            .rsaEncryptionOAEPSHA384AESGCM,
            .rsaEncryptionOAEPSHA512AESGCM,
            .eciesEncryptionStandardX963SHA1AESGCM,
            .eciesEncryptionStandardX963SHA224AESGCM,
            .eciesEncryptionStandardX963SHA256AESGCM,
            .eciesEncryptionStandardX963SHA384AESGCM,
            .eciesEncryptionStandardX963SHA512AESGCM,
            .eciesEncryptionCofactorX963SHA1AESGCM,
            .eciesEncryptionCofactorX963SHA224AESGCM,
            .eciesEncryptionCofactorX963SHA256AESGCM,
            .eciesEncryptionCofactorX963SHA384AESGCM,
            .eciesEncryptionCofactorX963SHA512AESGCM,
            .eciesEncryptionStandardVariableIVX963SHA224AESGCM,
            .eciesEncryptionStandardVariableIVX963SHA256AESGCM,
            .eciesEncryptionStandardVariableIVX963SHA384AESGCM,
            .eciesEncryptionStandardVariableIVX963SHA512AESGCM,
            .eciesEncryptionCofactorVariableIVX963SHA224AESGCM,
            .eciesEncryptionCofactorVariableIVX963SHA256AESGCM,
            .eciesEncryptionCofactorVariableIVX963SHA384AESGCM,
            .eciesEncryptionCofactorVariableIVX963SHA512AESGCM,
            .ecdhKeyExchangeStandard,
            .ecdhKeyExchangeStandardX963SHA1,
            .ecdhKeyExchangeStandardX963SHA224,
            .ecdhKeyExchangeStandardX963SHA256,
            .ecdhKeyExchangeStandardX963SHA384,
            .ecdhKeyExchangeStandardX963SHA512,
            .ecdhKeyExchangeCofactor,
            .ecdhKeyExchangeCofactorX963SHA1,
            .ecdhKeyExchangeCofactorX963SHA224,
            .ecdhKeyExchangeCofactorX963SHA256,
            .ecdhKeyExchangeCofactorX963SHA384,
            .ecdhKeyExchangeCofactorX963SHA512
        ]
        
        for a in algorithms {
            if SecKeyIsAlgorithmSupported(key, .decrypt, a) {
                print(">>>>>>>>>>>>> supported algorithm: \(a)")
            }
        }
        
        //==================
        
        let algorithm: SecKeyAlgorithm = .rsaEncryptionPKCS1 // .rsaSignatureMessagePKCS1v15SHA256 // .rsaSignatureDigestPKCS1v15SHA1 // .rsaSignatureDigestPKCS1v15Raw // .rsaSignatureDigestPKCS1v15SHA256 //.rsaEncryptionPKCS1
        
//        guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else {
//            print("Can't decrypt. Algorithm not supported.")
//            return nil
//        }

        var error: Unmanaged<CFError>? = nil
        let clearTextData = SecKeyCreateDecryptedData(key,
                                                      algorithm,
                                                      cipherTextData as CFData,
                                                      &error) as Data?
        if let error = error {
            print("Can't decrypt. \((error.takeRetainedValue() as Error).localizedDescription)")
            return nil
        }
        guard clearTextData != nil else {
            print("Can't decrypt. No resulting cleartextData.")
            return nil
        }
        print("Decrypted data.")
        return clearTextData
    }
Suge
  • 2,808
  • 3
  • 48
  • 79

1 Answers1

0

If your code only works on Mac OS, you may be able to create a Seckey by using the SecKeyCreateFromData method instead of the SecKeyCreateWithData method.

Here is Sample Code:

    func getPublicKey(from data: Data?) throws -> SecKey {
        var error: Unmanaged<CFError>? = nil
        guard let data = data else { throw error!.takeRetainedValue() as Error }
        let publicKeyMaybe = SecKeyCreateFromData([:] as CFDictionary, data as NSData, &error)
        guard let publicKey = publicKeyMaybe else {
            throw error!.takeRetainedValue() as Error
        }
        return publicKey
    }

And You should convert RSA public key string to data using ASCII encoding.

let pubKey = try! getPublicKey(from: kPubKey.data(using: .ascii))
Kazunori Takaishi
  • 2,268
  • 1
  • 15
  • 27
  • SecKeyCreateDecryptedData accepts a private key to execute decryption, how can I use a public key? – Suge Jan 25 '21 at 03:58
  • It seems that your code has an error at the point where it calls `SecKeyCreateWithData` to create a `SecKey`. This is because the value of the data type passed as argument is not appropriate. So you need to make sure that the string of `kPubKey` is properly encoded in Base64. – Kazunori Takaishi Jan 25 '21 at 07:50
  • If your code only works on Mac OS, you may be able to create a `Seckey` by using the `SecKeyCreateFromData` method instead of the `SecKeyCreateWithData` method. – Kazunori Takaishi Jan 25 '21 at 16:54
  • I tried it and succeeded in creating a `secKey` from an `RSA` string using `SecKeyCreateFromData `. – Kazunori Takaishi Jan 25 '21 at 17:06
  • @Suge I updated the answer. Please try it! – Kazunori Takaishi Jan 26 '21 at 05:59
  • Thank you mate, but it's failed to decrypt at `let decryptedData = decrypt(key: pubKey, data: decodedData)!`, any idea? – Suge Jan 26 '21 at 08:01
  • Oh, really? Do you know where it fails in the method `decrypt(key: pubKey, data: decodedData)`? What error message did you get? – Kazunori Takaishi Jan 26 '21 at 14:52
  • Yes, Pls look at the the code I updated, it tested all the algorithm, and none of them is supported. I got error message `The operation couldn’t be completed. (OSStatus error -50 - MacOS error: -50)` while try to decrypt. Have you gotten success? Thank you! – Suge Jan 27 '21 at 02:39