Search code examples
cobjective-cmacossecuritycertificate

How do I retrieve Subject or Issuer Distinguished Name from a certificate with Apple Security framework?


I wanted to use the SecCertificateCopyValues function but could not find the correct certificate OID. https://developer.apple.com/documentation/security/certificate_key_and_trust_services/certificates/certificate_oids?language=objc

Should I have used some other function?


Solution

  • Yes, you should use SecCertificateCopyValues. In this returned dictionary, search for kSecOIDX509V1IssuerName from the Apple documentation link you mentioned. In the documentation of SecCertificateCopyValues there is mentioned, that

    Each entry in this dictionary is itself a dictionary with the keys described in Certificate Property Keys.

    So, for example, to get the common name of the issuer, you would:

    • retrieve a dictionary for the key kSecOIDX509V1IssuerName
    • from there get the value array with the key kSecPropertyKeyValue
    • each array element itself is a dictionary
    • e.g. for the common name you want to use the key kSecOIDCommonName

    To create a distinguished name you can check for different keys.

    If you have an OID, you can look at the corresponding constant in Apple's open source certificate values and add to the code to get the values you are interested in:

    In code, this might look like this:

    static CFStringRef createDistinguishedName(CFDictionaryRef certValues, CFStringRef key) {
        CFDictionaryRef dict = CFDictionaryGetValue(certValues, key);
        CFArrayRef values = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
        
        CFMutableStringRef dn = CFStringCreateMutableCopy(NULL, 0, CFSTR(""));
        
        CFStringRef keys[] = { kSecOIDCommonName, kSecOIDOrganizationName, kSecOIDOrganizationalUnitName, kSecOIDLocalityName, kSecOIDStateProvinceName, kSecOIDCountryName, kSecOIDSerialNumber, kSecOIDEmailAddress};
        CFStringRef txt[] = { CFSTR("CN"), CFSTR("O"), CFSTR("OU"), CFSTR("L"), CFSTR("S"), CFSTR("C"), CFSTR("SERIALNUMBER"), CFSTR("MAIL")};
        
        bool appendComa = false;
        for(int i = 0; i < CFArrayGetCount(values); i++) {
            CFDictionaryRef subDict = CFArrayGetValueAtIndex(values, i);
            CFStringRef labelVal = CFDictionaryGetValue(subDict, kSecPropertyKeyLabel);
            
            for(int k = 0; k < sizeof(keys)/sizeof(keys[0]); k++) {
                if (kCFCompareEqualTo == CFStringCompare(labelVal, keys[k], 0)) {
                    if (appendComa)
                        CFStringAppend(dn, CFSTR(", "));
                    CFStringAppend(dn, txt[k]);
                    CFStringAppend(dn, CFSTR("="));
                    CFStringAppend(dn, CFDictionaryGetValue(subDict, kSecPropertyKeyValue));
                    appendComa = true;
                }
            }
        }
        return dn;
    }
    

    It can be called like this:

    void printDN(CFDataRef certData) {
        SecCertificateRef certRef = SecCertificateCreateWithData(NULL, certData);
        CFDictionaryRef certValues = SecCertificateCopyValues(certRef, nil, nil);
        CFStringRef dn = createDistinguishedName(certValues, kSecOIDX509V1IssuerName);
        CFShow(dn);
        CFRelease(dn);
        CFRelease(certValues);
        CFRelease(certRef);
    }
    

    A sample certificate from here would result in the following string:

    C=JP, S=Tokyo, L=Chuo-ku, O=Frank4DD, OU=WebCert Support, CN=Frank4DD Web CA, MAIL=support@frank4dd.com