使用EC密钥

时间:2017-02-22 18:59:35

标签: ios swift swift3

我正在尝试编写一个生成一组Ecliptic Curve键(256位)的原型,然后使用私钥对消息进行签名。我有生成和管理密钥的代码,但是当我尝试调用SecKeyRawSign时,我得到-50 errSecParam错误。生成密钥的代码如下所示:

private func generateKeyPair() throws {
    var error: Unmanaged<CFError>? = nil
    let acl = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                              [.touchIDAny, .privateKeyUsage], &error)

    guard error == nil else {
        throw MessageError(message: "Could not create ACL: \(error)")
    }

    // We don't want the public key stored in the ecure enclave, so we create it as
    // non permament and add it manually to the keychain later
    let publicKeyParameters: [CFString: Any] = [
        kSecAttrIsPermanent: false,
        kSecAttrApplicationTag: ViewController.KeyTag,
        kSecAttrLabel: ViewController.PublicLabel
    ]

    let privateKeyParameters: [CFString: Any] = [
        kSecAttrIsPermanent: true,
        kSecAttrApplicationTag: ViewController.KeyTag,
        kSecAttrLabel: ViewController.PrivateLabel,
        kSecAttrAccessControl: acl!
    ]

    var parameters: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeEC,
        kSecAttrKeySizeInBits: NSNumber(value: 256),
        kSecPublicKeyAttrs: publicKeyParameters,
        kSecPrivateKeyAttrs: privateKeyParameters
    ]

    // On the simulator we can't use the Secure Enclave
    if hasSecureEnclave() {
        parameters[kSecAttrTokenID] = kSecAttrTokenIDSecureEnclave
    }

    var pubKeyRef, privKeyRef: SecKey?
    var result = SecKeyGeneratePair(parameters as CFDictionary, &pubKeyRef, &privKeyRef)
    guard result == noErr else {
        throw MessageError(message: "Could not create key pair: \(result)")
    }

    parameters = [
        kSecClass: kSecClassKey,
        kSecAttrKeyType: kSecAttrKeyTypeEC,
        kSecAttrApplicationTag: ViewController.KeyTag,
        kSecAttrLabel: ViewController.PublicLabel,
        kSecAttrKeyClass: kSecAttrKeyClassPublic,
        kSecValueRef: pubKeyRef!
    ]

    result = SecItemAdd(parameters as CFDictionary, nil)
    guard result == noErr else {
        throw MessageError(message: "Could not add public key to keychain: \(result)")
    }
}

签名代码如下:

private func signWithPrivateKey(_ text: String, _ key: SecKey) throws -> String? {
    var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
    let data = text.data(using: .utf8)!

    let _ = digest.withUnsafeMutableBytes { digestBytes in
        data.withUnsafeBytes { dataBytes in
            CC_SHA256(dataBytes, CC_LONG(data.count), digestBytes)
        }
    }

    var signature = Data(count: SecKeyGetBlockSize(key))
    var signatureLength = signature.count

    let result = signature.withUnsafeMutableBytes { signatureBytes in
        digest.withUnsafeBytes { digestBytes in
            SecKeyRawSign(key,
                          SecPadding.PKCS1SHA256,
                          digestBytes,
                          digest.count,
                          signatureBytes,
                          &signatureLength)
        }
    }

    guard result == noErr else {
        throw MessageError(message: "Could not sign data: \(result)")
    }

    return signature.base64EncodedString()
}

显然,签名功能中的最后一名守卫正在被绊倒,而且它正在返回errSecParam。

有没有人使用EC密钥在iOS中成功完成数据签名?如果是这样,你在这看到什么明显的东西吗?切向有一种方法可以获得有关错误本身的更多信息。

编辑:要添加一个重要的细节,如果我什么都不做,只是更改此代码来生成2048位RSA密钥,代码工作正常。密钥生成并且消息已签名。只有256位EC密钥才会失败。在iOS中有没有其他方法可以做ECDSA?

1 个答案:

答案 0 :(得分:2)

我解决了这个问题。我创建的用于保存签名的缓冲区太小了。我将其更改为使用SecKeyGetBlockSize()* 4,然后在调用后将缓冲区减少为signatureLenght。我唯一的问题是,如果有更好的方法来计算长度(除了调用SecKeyRawSign,让它失败,然后调整缓冲区大小到返回的大小)。

新的标志代码如下所示:

private func signWithPrivateKey(_ text: String, _ key: SecKey) throws -> String? {
    var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
    let data = text.data(using: .utf8)!

    let _ = digest.withUnsafeMutableBytes { digestBytes in
        data.withUnsafeBytes { dataBytes in
            CC_SHA256(dataBytes, CC_LONG(data.count), digestBytes)
        }
    }

    var signature = Data(count: SecKeyGetBlockSize(key) * 4)
    var signatureLength = signature.count

    let result = signature.withUnsafeMutableBytes { signatureBytes in
        digest.withUnsafeBytes { digestBytes in
            SecKeyRawSign(key,
                          SecPadding.PKCS1SHA256,
                          digestBytes,
                          digest.count,
                          signatureBytes,
                          &signatureLength)
        }
    }

    let count = signature.count - signatureLength
    signature.removeLast(count)

    guard result == noErr else {
        throw MessageError(message: "Could not sign data: \(result)")
    }

    return signature.base64EncodedString()
}