iOS: Verifying a File With a Certificate and Signa

2019-03-16 17:58发布

问题:

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?

回答1:

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");


回答2:

Your problem is when you're trying to get the key data:

SecKeyRef publicKey;
publicKey = SecTrustCopyPublicKey(trust);
size_t keysize = SecKeyGetBlockSize(publicKey);
But the content of publicKey

NSData* keyData = [NSData dataWithBytes:publicKey length:keysize];

That is not the public key. That is the first "X" bytes of the SecKeyRef data structure ("X" being the size of the public key). That's nothing particularly useful.

Unfortunately, I know of no way to go directly from a SecKeyRef to an NSData. What you have to do is put the SecKeyRef into keychain (SecItemAdd), and then fetch it back (SecItemCopyMatching with kSecReturnData set). When you fetch it back, then you'll have the public key as an NSData.

(I have no idea why Apple makes Security.framework so insanely complicated to use....)