Search code examples
copensslx509

How to calculate X.509 certificate's SHA-1 fingerprint in C/C++/Objective-C?


Background:

I am writing a client utility which is capable of connecting to a remote server using SSL/TLS. The client uses OpenSSL to perform the SSL/TLS transactions and I would like to allow users to specify authorized CA Certs (in the case of self signed certs or private CA setups) used to sign the server's certificate. I plan on using the cert's fingerprint, common name, and validity dates to allow the user to quickly view the certs the client uses to validate servers.

Question:

How do you calculate the SHA1 hash/fingerprint of an X509 cert stored within a PEM file using C/C++/Objective-C?

After days of search and experimenting I found a solution and will post it as an answer, however I welcome better or more correct solutions.


Solution

  • I found below to yield identical output to above:

    +(NSData *)sha1:(SecCertificateRef) cert {
        // fingerprint is over canonical DER rep.
        CFDataRef data = SecCertificateCopyData(cert);
        NSData * out = [[NSData dataWithBytes:CFDataGetBytePtr(data) length:CFDataGetLength(data)] sha1Digest];
        CFRelease(data);
        return out;
    }
    

    which is a bit shorter in objective C. It needs the below extensions to NSData/NSString though to get the formatting close to Netscape, OSX or Windows.

    - (NSData *)md5Digest
    {
        unsigned char result[CC_MD5_DIGEST_LENGTH];
    
        CC_MD5([self bytes], (CC_LONG)[self length], result);
        return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
    }
    
    - (NSData *)sha1Digest
    {
        unsigned char result[CC_SHA1_DIGEST_LENGTH];
    
        CC_SHA1([self bytes], (CC_LONG)[self length], result);
        return [NSData dataWithBytes:result length:CC_SHA1_DIGEST_LENGTH];
    }
    
    - (NSString *)hexStringValue
    {
        NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 2)];
    
        const unsigned char *dataBuffer = [self bytes];
        int i;
    
        for (i = 0; i < [self length]; ++i)
        {
            [stringBuffer appendFormat:@"%02lx", (unsigned long)dataBuffer[i]];
        }
    
        return [stringBuffer copy];
    }
    
    
    - (NSString *)hexColonSeperatedStringValue
    {
        return [self hexColonSeperatedStringValueWithCapitals:YES];
    }
    
    - (NSString *)hexColonSeperatedStringValueWithCapitals:(BOOL)capitalize {
        NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 3)];
    
        const unsigned char *dataBuffer = [self bytes];
        NSString * format = capitalize ? @"%02X" : @"%02x";
        int i;
    
        for (i = 0; i < [self length]; ++i)
        {
            if (i) 
                [stringBuffer appendString:@":"];
            [stringBuffer appendFormat:format, (unsigned long)dataBuffer[i]];
        }
    
        return [stringBuffer copy];
    }