I have an x509 certificate as a file/byte array that I'd like to use to verify the signature provided in a CertificateVerify
TLS message. I think I can use SecKeyVerifySignature
once I've determined the certificate's key algorithm (SecKeyAlgorithm
parameter) and initialized the signedData
from the transcript hash (concatenated to the context string, etc.).
openssl x509
reports the certificate's key like
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:44:58:8c:d0:95:90:14:45:82:db:4f:56:41:7d:
57:0e:f5:b4:d8:65:04:6c:21:5a:cd:1e:0e:87:10:
f9:31:c6:fa:b9:ad:b3:a5:e1:df:9f:32:25:4b:a9:
40:5c:d4:56:0d:bb:55:fd:f4:68:f9:4e:89:70:56:
b9:1c:4a:ef:93
ASN1 OID: prime256v1
NIST CURVE: P-256
I believe I can parse the certificate with the mechanism described here, eg.
CFDataRef certData = CFDataCreate(NULL, (const UInt8*) rawCert, len);
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, certData);
And I think I can use SecCertificateCopyKey
to extract the key, eg.
SecKeyRef key = SecCertificateCopyKey(certificate);
I can't, however, find a way to extract the key's signature algorithm (Public Key Algorithm). I found SecKeyIsAlgorithmSupported
. Do I need to iterate over al the possible SecKeyAlgorithm
constants to find the one that the key is using (ie. a SecKeyAlgorithm
for id-ecPublicKey
)?
I misunderstood my own goals.
The CertificateVerify
message provides a digest of the handshake up to that point. The server uses its certificate's private key to perform that signature. As indicated in the TLS 1.3 specification, the signature algorithm is part of the CertificateVerify structure
struct {
SignatureScheme algorithm;
opaque signature<0..2^16-1>;
} CertificateVerify;
I just need to extract it and convert it to a SecKeyAlgorithm
. For example (with C++)
SecKeyAlgorithm keyAlgorithm;
// algorithm extracted from CertificateVerify
switch (algorithm) {
case SignatureScheme::ecdsa_secp256r1_sha256:
keyAlgorithm = kSecKeyAlgorithmECDSASignatureDigestX962SHA256;
break;
case SignatureScheme::rsa_pss_sha256:
keyAlgorithm = kSecKeyAlgorithmRSASignatureDigestPSSSHA256;
break;
case SignatureScheme::ed25519:
case SignatureScheme::ed448:
default:
throw std::runtime_error("unsupported peer cert type");
}
I can then confirm the certificate supports that algorithm
if (!SecKeyIsAlgorithmSupported(key, kSecKeyOperationTypeVerify, keyAlgorithm)) {
CFRelease(publicKey);
throw std::runtime_error("Unsupported signature scheme");
}
and finally perform the verification with the signature
in the CertificateVerify
and the compiled signed data from the handshake
CFErrorRef error;
bool signatureVerified = SecKeyVerifySignature(key, keyAlgorithm, toBeSignedData, signature, &error);
if (!signatureVerified) {
CFRelease(error); // or use it
throw std::runtime_error("Signature verification failed");
}