nRewik
nRewik

Reputation: 9148

How to sign/verify using SecKeyRawSign with elliptic curve key

I generated an elliptic curve private/public key pair on iOS, and store them to keychain inside secure enclave.

I want to sign/verify a message with those keys.

So, here is my code to sign a message using SecKeyCreateSignature.

var error: Unmanaged<CFError>?
let signature = SecKeyCreateSignature(myPrivateKey,
                                      .ecdsaSignatureMessageX962SHA512,
                                      plainData as CFData,
                                      &error)

It works well, but SecKeyCreateSignature is only available in iOS 10.

I would like to maintain compatibility with iOS 9 at least. So I searched the alternative way to sign a message, and I found SecKeyRawSign that look similar to the function above.

However SecKeyRawSign doesn't look like to support elliptic curve key.

How can I use SecKeyRawSign or alternative ways to sign/verify a message that is equivalent or similar to the code above?

Upvotes: 4

Views: 3342

Answers (1)

nRewik
nRewik

Reputation: 9148

For compatibility you can use SecKeyRawSign similarly to SecKeyCreateSignature.

In this case, they are not equivalent because the algorithm to create the signature is different. But they can sign/verify as usual.


func signCompat(privateKey: SecKey, rawData: Data) -> Data? {
    if #available(iOS 10.0, *) {
        return sign_iOS_10(privateKey: privateKey, rawData: rawData)
    } else {
        return sign_iOS_9(privateKey: privateKey, rawData: rawData)
    }
}


@available(iOS 10.0, *)
func sign_iOS_10(privateKey: SecKey, rawData: Data) -> Data? {
    let algorithm = SecKeyAlgorithm.ecdsaSignatureMessageX962SHA512
    return SecKeyCreateSignature(privateKey, algorithm, rawData as CFData, nil) as Data?
}

func sign_iOS_9(privateKey: SecKey, rawData: Data) -> Data? {

    let sha512digestedData = rawData.sha512()
    var raw_signature_length = 512
    let raw_signature_bytes = UnsafeMutablePointer<UInt8>.allocate(capacity: 512)

    let osStatus = SecKeyRawSign(privateKey,
                                    .PKCS1SHA512,
                                    [UInt8](sha512digestedData),
                                    Int(CC_SHA512_DIGEST_LENGTH),
                                    raw_signature_bytes,
                                    &raw_signature_length)

    guard osStatus == errSecSuccess else { return nil }
    return Data(bytes: raw_signature_bytes, count: raw_signature_length)
}

// The same logic is applied to verify
//
func verifyCompat(privateKey: SecKey, rawData: Data) -> Data? { ... }

@available(iOS 10.0, *)
func verify_iOS_10(privateKey: SecKey, rawData: Data) -> Data? { ... }

func verify_iOS_9(privateKey: SecKey, rawData: Data) -> Data? { ... }

You can check for correctness with these openssl commands.

// Sign
openssl dgst -sha512 -sign private.pem < test.pdf > signature.bin

// Verify
openssl dgst -sha512 -verify public.pem -signature signature.bin test.pdf

Upvotes: 4

Related Questions