kSecAttrKeyTypeEC swift

我的未来我决定 提交于 2019-12-08 07:04:35

问题


I try to get sha384 with ECDSA keys. I follow up with this article: https://digitalleaves.com/blog/2015/10/asymmetric-cryptography-in-swift/?replytocom=52690#respond

but I change RSA to EC, of course.

and keys are generated, but encryptMessageWithPublicKey doesn't work :(

Variable "status" in method: encryptMessageWithPublicKey return -50 ...

Here is my code:

import UIKit
import Security

private let _singletonInstance = AsymmetricCryptoManager()


private let kAsymmetricCryptoManagerApplicationTag = "com.AsymmetricCrypto.keypair"
private let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeEC//kSecAttrKeyTypeEC
private let kAsymmetricCryptoManagerKeySize = 384

// private key parameters
let privateKeyParams: [String: AnyObject] = [
    kSecAttrIsPermanent as String: true as AnyObject,
    kSecAttrApplicationTag as String: "com.AsymmetricCrypto.keypair" as AnyObject
]

// private key parameters
let publicKeyParams: [String: AnyObject] = [
    kSecAttrIsPermanent as String: true as AnyObject,
    kSecAttrApplicationTag as String: "com.AsymmetricCrypto.keypair" as AnyObject
]

enum AsymmetricCryptoException: Error {
    case unknownError
    case duplicateFoundWhileTryingToCreateKey
    case keyNotFound
    case authFailed
    case unableToAddPublicKeyToKeyChain
    case wrongInputDataFormat
    case unableToEncrypt
    case unableToDecrypt
    case unableToSignData
    case unableToVerifySignedData
    case unableToPerformHashOfData
    case unableToGenerateAccessControlWithGivenSecurity
    case outOfMemory
}



class AsymmetricCryptoManager: NSObject {

    /** Shared instance */
    class var sharedInstance: AsymmetricCryptoManager {
        return _singletonInstance
    }


    func createSecureKeyPair(_ completion: ((_ success: Bool, _ error: AsymmetricCryptoException?) -> Void)? = nil) {
        // private key parameters
        let privateKeyParams: [String: AnyObject] = [
            kSecAttrIsPermanent as String: true as AnyObject,
            kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag as AnyObject
        ]

        // private key parameters
        let publicKeyParams: [String: AnyObject] = [
            kSecAttrIsPermanent as String: true as AnyObject,
            kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag as AnyObject
        ]

        // global parameters for our key generation
        let parameters: [String: AnyObject] = [
            kSecAttrKeyType as String:          kAsymmetricCryptoManagerKeyType,
            kSecAttrKeySizeInBits as String:    kAsymmetricCryptoManagerKeySize as AnyObject,
            kSecPublicKeyAttrs as String:       publicKeyParams as AnyObject,
            kSecPrivateKeyAttrs as String:      privateKeyParams as AnyObject,
            ]

        // asynchronously generate the key pair and call the completion block
        DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in
            var pubKey, privKey: SecKey?
            let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)

            if status == errSecSuccess {
                DispatchQueue.main.async(execute: { completion?(true, nil) })
            } else {
                var error = AsymmetricCryptoException.unknownError
                switch (status) {
                case errSecDuplicateItem: error = .duplicateFoundWhileTryingToCreateKey
                case errSecItemNotFound: error = .keyNotFound
                case errSecAuthFailed: error = .authFailed
                default: break
                }
                DispatchQueue.main.async(execute: { completion?(false, error) })
            }
        }
    }



    private func getPublicKeyReference() -> SecKey? {
        let parameters = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: kSecAttrKeyTypeEC,
            kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag,
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecReturnRef as String: true,
            ] as [String : Any]
        var ref: AnyObject?
        let status = SecItemCopyMatching(parameters as CFDictionary, &ref)
        if status == errSecSuccess { return ref as! SecKey? }
        else { return nil }
    }


    func encryptMessageWithPublicKey(_ message: String, completion: @escaping (_ success: Bool, _ data: Data?, _ error: AsymmetricCryptoException?) -> Void) {
        DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in

            if let publicKeyRef = self.getPublicKeyReference() {
                // prepare input input plain text
                guard let messageData = message.data(using: String.Encoding.utf8) else {
                    completion(false, nil, .wrongInputDataFormat)
                    return
                }


                let plainText = (messageData as NSData).bytes.bindMemory(to: UInt8.self, capacity: messageData.count)
                let plainTextLen = messageData.count

                // prepare output data buffer
                var cipherData = Data(count: SecKeyGetBlockSize(publicKeyRef))
                let cipherText = cipherData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8> in
                    return bytes
                })


                var cipherTextLen = cipherData.count

                let status = SecKeyEncrypt(publicKeyRef, .PKCS1SHA384, plainText, plainTextLen, cipherText, &cipherTextLen)

                // analyze results and call the completion in main thread
                DispatchQueue.main.async(execute: { () -> Void in
                    completion(status == errSecSuccess, cipherData, status == errSecSuccess ? nil : .unableToEncrypt)
                    cipherText.deinitialize()
                })
                return
            } else {


                DispatchQueue.main.async(execute: { completion(false, nil, .keyNotFound) }) }
        }
    }


    func decryptMessageWithPrivateKey(_ encryptedData: Data, completion: @escaping (_ success: Bool, _ result: String?, _ error: AsymmetricCryptoException?) -> Void) {
        DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in

            if let privateKeyRef = self.getPublicKeyReference() {
                // prepare input input plain text
                let encryptedText = (encryptedData as NSData).bytes.bindMemory(to: UInt8.self, capacity: encryptedData.count)
                let encryptedTextLen = encryptedData.count

                // prepare output data buffer
                var plainData = Data(count: 1024)
                let plainText = plainData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8> in
                    return bytes
                })
                var plainTextLen = plainData.count

                let status = SecKeyDecrypt(privateKeyRef, .PKCS1SHA384, encryptedText, encryptedTextLen, plainText, &plainTextLen)

                // analyze results and call the completion in main thread
                DispatchQueue.main.async(execute: { () -> Void in
                    if status == errSecSuccess {
                        // adjust NSData length
                        plainData.count = plainTextLen
                        // Generate and return result string
                        if let string = NSString(data: plainData as Data, encoding: String.Encoding.utf8.rawValue) as String? {
                            completion(true, string, nil)
                        } else { completion(false, nil, .unableToDecrypt) }
                    } else { completion(false, nil, .unableToDecrypt) }
                    plainText.deinitialize()
                })
                return
            } else { DispatchQueue.main.async(execute: { completion(false, nil, .keyNotFound) }) }
        }
    }


}



class ViewController: UIViewController {

    override func viewDidLoad() {
        AsymmetricCryptoManager.sharedInstance.createSecureKeyPair({ success, err in

            if success {

                AsymmetricCryptoManager.sharedInstance.encryptMessageWithPublicKey("thisShouldWork", completion: { (success, data, error) in
                    print(success)
                })

            }

            })

    }

}

回答1:


1) read please first https://www.apple.com/business/docs/iOS_Security_Guide.pdf 2) use generated ECDSA key for signing

import Foundation
import Security

private let kAsymmetricCryptoManagerApplicationTag = "com.AsymmetricCrypto.keypair"
private let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeEC
private let kAsymmetricCryptoManagerKeySize = 384

// private key parameters
let privateKeyParams: [String: AnyObject] = [
    kSecAttrIsPermanent as String: false as AnyObject,
    kSecAttrApplicationTag as String: "com.AsymmetricCrypto.keypair" as AnyObject
]


// private key parameters
let publicKeyParams: [String: AnyObject] = [
    kSecAttrIsPermanent as String: false as AnyObject,
    kSecAttrApplicationTag as String: "com.AsymmetricCrypto.keypair" as AnyObject
]

// parameters
let parameters: [String: AnyObject] = [
    kSecAttrKeyType as String:          kAsymmetricCryptoManagerKeyType,
    kSecAttrKeySizeInBits as String:    kAsymmetricCryptoManagerKeySize as AnyObject,
    kSecPublicKeyAttrs as String:       publicKeyParams as AnyObject,
    kSecPrivateKeyAttrs as String:      privateKeyParams as AnyObject,
]

var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)

if let privKey = privKey, let pubKey = pubKey  {

    let message = "qwerty"
    let messageData = message
        .utf8CString
        .map{ UInt8(bitPattern: $0) }

    var sign = [UInt8](repeating: 0, count:  256)
    var length = sign.count

    let status = SecKeyRawSign(privKey, .PKCS1SHA384, messageData, messageData.count, &sign, &length)
    print("message: \"\(message)\" was sign with status:", status == errSecSuccess ? "OK": "KO")

    let verified = SecKeyRawVerify(pubKey, .PKCS1SHA384, messageData, messageData.count, sign, length)
    print("message: \"\(message)\" was", verified == errSecSuccess ? "unmodified\t" : "modified  \t", messageData)

    // modified messageData
    var modified = messageData
    modified[0] = messageData[0] + 1

    let modifiedMessage = String(cString: modified)

    let verified2 = SecKeyRawVerify(pubKey, .PKCS1SHA384, modified, modified.count, sign, length)
    print("message: \"\(modifiedMessage)\" was", verified2 == errSecSuccess ? "unmodified" : "modified  \t", modified)
}

it prints

message: "qwerty" was sign with status: OK
message: "qwerty" was unmodified     [113, 119, 101, 114, 116, 121, 0]
message: "rwerty" was modified       [114, 119, 101, 114, 116, 121, 0]

In real applications the

  • a) message is encrypted with some symmetric key (AES, ...)
  • b) the key is encrypted with RSA (SecKeyEncrypt)
  • c) encrypted message + encryptedKey are signed with ECDSA
  • d) everything is delivered to receiver
  • e) receiver check signature
  • f) decrypt encryptedKey (SecKeyDecrypt)
  • g) decrypt the message (AES, ...)

to check what is supported, use

SecKeyIsAlgorithmSupported(pubKey, .verify, .ecdsaSignatureRFC4754) == true

Another way to sign and verify data

if let cdata = CFDataCreate(kCFAllocatorDefault, messageData, messageData.count),
        // create signature
        let csign = SecKeyCreateSignature(privKey, .ecdsaSignatureRFC4754, cdata, nil) {

        // transfer messageData, csign

        // verify signature
        let verified = SecKeyVerifySignature(pubKey, .ecdsaSignatureRFC4754, cdata, csign, nil)
        print(cdata, csign, verified)
    }

For Encryption / Decryption use ECIES or RSA

SecKeyIsAlgorithmSupported(pubKey, .encrypt, .eciesEncryptionCofactorX963SHA384AESGCM)
SecKeyIsAlgorithmSupported(privKey, .decrypt, .eciesEncryptionCofactorX963SHA384AESGCM)

return true with your key pair. Encrypt / Decrypt with your key pair

if let cdata = CFDataCreate(kCFAllocatorDefault, messageData, messageData.count) {
        print("data", cdata)
        if let coded = SecKeyCreateEncryptedData(pubKey, .eciesEncryptionCofactorX963SHA384AESGCM, cdata, nil) {
            print("\ncoded",coded)

            if let decoded = SecKeyCreateDecryptedData(privKey, .eciesEncryptionCofactorX963SHA384AESGCM, coded, nil)
            {
                print("\ndecoded", decoded)
            }
        }
    }

prints

data <71776572 747900>

coded <04ebbf3a 4bb5b767 3251f4dc d131f7be 11516a92 4c8b4f66 23d08751 5ab45cc2 a97e908d 0e689e02 81b74ba0 f9181b24 5a90dea0 052ef3ef 4be6027d 1b67ab71 cf123561 4dfdac16 837f3b6c 195f5122 985bbedb 9dd2e52e e1828f08 4ce6ecb8 6b089205 c820bf0f f8dc6620 24fa53ed 75431006 a6f14c9d>

decoded <71776572 747900>

for more info read https://www.ietf.org/rfc/rfc5289.txt




回答2:


So the answer is really simple..

Apple's implementation only supports ECDSA, which can be used for signing but not encryption.




回答3:


errorStatus of -50 means your key size is too large. For a reference, look for "errSecKeySizeNotAllowed" here: https://www.osstatus.com/search/results?platform=all&framework=all&search=-50

Apples kSecAttrKeyTypeECSECPrimeRandom and kSecAttrKeyTypeEC only supports a key size of 256, inside and outside of the secure enclave, https://developer.apple.com/library/content/documentation/Security/Conceptual/CertKeyTrustProgGuide/SecureKeyGen.html, so you need to make the following change to work:

kAsymmetricCryptoManagerKeySize = 256

Larger key sizes are allowed for RSA, which is why you are not getting an error. A 256 bit EC key is said to be as secure as a 3072 bit RSA key.




回答4:


Make sure you have added the "Security.framework" in your project under General > Linked Frameworks and Libraries.



来源:https://stackoverflow.com/questions/43893045/ksecattrkeytypeec-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!