iOS: Verifying a File With a Certificate and Signature - Public Key is Wrong, Verification Fails

encryption, ios, keychain, objective-c

Solution

I solved the problem with the help of a hint from Apple Dev Forums.

The problem had nothing to do with the keychain. But I passed the wrong parameters to the verification function. It needs a digest (hash) of the data, not the data directly.

NSData* fileData = [NSData dataWithContentsOfFile:(...)];
NSData* signatureData = [NSData dataWithContentsOfFile:(...)];
NSData* certificateData = [NSData dataWithContentsOfFile:(...)];  

SecCertificateRef certificateFromFile = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData); // load the certificate

SecPolicyRef secPolicy = SecPolicyCreateBasicX509();

SecTrustRef trust;
OSStatus statusTrust = SecTrustCreateWithCertificates( certificateFromFile, secPolicy, &trust);
SecTrustResultType resultType;
OSStatus statusTrustEval =  SecTrustEvaluate(trust, &resultType);
SecKeyRef publicKey = SecTrustCopyPublicKey(trust);

uint8_t sha1HashDigest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1([fileData bytes], [fileData length], sha1HashDigest);

OSStatus verficationResult = SecKeyRawVerify(publicKey,  kSecPaddingPKCS1SHA1, sha1HashDigest, CC_SHA1_DIGEST_LENGTH, [signatureData bytes], [signatureData length]);
CFRelease(publicKey);
CFRelease(trust);
CFRelease(secPolicy);
CFRelease(certificateFromFile);
if (verficationResult == errSecSuccess) NSLog(@"Verified");

Problem

I have three things: a file, a signature file, and a X509 certificate file .cer. The file has to be verified using the public key in the certificate and the signature file. I want to do it using Security.h/CommonCrypto. What I tried so far: ``` // load all the files NSData* fileData = [NSData dataWithContentsOfFile:(...)]; NSData* signatureData = [NSData dataWithContentsOfFile:(...)]; NSData* certificateData = [NSData dataWithContentsOfFile:(...)]; SecCertificateRef certificate = SecCertificateCreateWithData(NULL, CFBridgingRetain(certificateData)); // load the certificate ``` The certificate loads just fine. It's name can be checked using ``` CFStringRef certificateDescription = SecCertificateCopySubjectSummary(certificate); ``` which works. As there seems to be no method on iOS to copy the public key directly, I first create a trust. ``` SecTrustRef trust; OSStatus statusTrust = SecTrustCreateWithCertificates( certificate, secPolicy, &trust); SecTrustResultType resultType; OSStatus statusTrustEval = SecTrustEvaluate(trust, &resultType); ``` This all works fine with a errSecSuccess result. Now I try to get the public key. ``` SecKeyRef publicKey; publicKey = SecTrustCopyPublicKey(trust); size_t keysize = SecKeyGetBlockSize(publicKey); ``` But the content of publicKey ``` NSData* keyData = [NSData dataWithBytes:publicKey length:keysize]; ``` is not the same as the public key I see when opening the .cer file. So this is problem number one. Then I try to verify the signature, even though I know the public key is wrong. The padding is correct. ``` OSStatus verficationResult = SecKeyRawVerify(publicKey, kSecPaddingPKCS1, [fileData bytes], [fileData length], [signatureData bytes], [signatureData length]); ``` This fails with a OSStatus of -9809 (The operation couldn’t be completed). I expect it to be –25293 errSecAuthFailed. Am I doing something fundamentally wrong?

Original source