printCert.c   [plain text]


/*
 * Copyright (c) 2003-2008,2011-2012 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * printCert.c - utility functions for printing certificate info
 */

#include "printCert.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecTrustPriv.h>

void fprint_string(CFStringRef string, FILE *file) {
    UInt8 buf[256];
    CFRange range = { .location = 0 };
    range.length = CFStringGetLength(string);
    while (range.length > 0) {
        CFIndex bytesUsed = 0;
        CFIndex converted = CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, false, buf, sizeof(buf), &bytesUsed);
        fwrite(buf, 1, bytesUsed, file);
        range.length -= converted;
        range.location += converted;
    }
}

void print_line(CFStringRef line) {
    fprint_string(line, stdout);
    fputc('\n', stdout);
}

static void printPlist(CFArrayRef plist, CFIndex indent, CFIndex maxWidth) {
    CFIndex count = CFArrayGetCount(plist);
    CFIndex ix;
    for (ix = 0; ix < count ; ++ix) {
        CFDictionaryRef prop = (CFDictionaryRef)CFArrayGetValueAtIndex(plist,
            ix);
        CFStringRef pType = (CFStringRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyType);
        CFStringRef label = (CFStringRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyLabel);
        CFStringRef llabel = (CFStringRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyLocalizedLabel);
        CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyValue);

        bool isSection = CFEqual(pType, kSecPropertyTypeSection);
        CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
        CFIndex jx = 0;
        for (jx = 0; jx < indent; ++jx) {
            CFStringAppend(line, CFSTR("    "));
        }
        if (llabel) {
            CFStringAppend(line, llabel);
            if (!isSection) {
                for (jx = CFStringGetLength(llabel) + indent * 4;
                    jx < maxWidth; ++jx) {
                    CFStringAppend(line, CFSTR(" "));
                }
                CFStringAppend(line, CFSTR(" : "));
            }
        }
        if (CFEqual(pType, kSecPropertyTypeWarning)) {
            CFStringAppend(line, CFSTR("*WARNING* "));
            CFStringAppend(line, (CFStringRef)value);
        } else if (CFEqual(pType, kSecPropertyTypeError)) {
            CFStringAppend(line, CFSTR("*ERROR* "));
            CFStringAppend(line, (CFStringRef)value);
        } else if (CFEqual(pType, kSecPropertyTypeSuccess)) {
            CFStringAppend(line, CFSTR("*OK* "));
            CFStringAppend(line, (CFStringRef)value);
        } else if (CFEqual(pType, kSecPropertyTypeTitle)) {
            CFStringAppend(line, CFSTR("*"));
            CFStringAppend(line, (CFStringRef)value);
            CFStringAppend(line, CFSTR("*"));
        } else if (CFEqual(pType, kSecPropertyTypeSection)) {
        } else if (CFEqual(pType, kSecPropertyTypeData)) {
            CFDataRef data = (CFDataRef)value;
            CFIndex length = CFDataGetLength(data);
            if (length > 20)
                CFStringAppendFormat(line, NULL, CFSTR("[%d bytes] "), length);
            const UInt8 *bytes = CFDataGetBytePtr(data);
            for (jx = 0; jx < length; ++jx) {
                if (jx == 0)
                    CFStringAppendFormat(line, NULL, CFSTR("%02X"), bytes[jx]);
                else if (jx < 15 || length <= 20)
                    CFStringAppendFormat(line, NULL, CFSTR(" %02X"),
                        bytes[jx]);
                else {
                    CFStringAppend(line, CFSTR(" ..."));
                    break;
                }
            }
        } else if (CFEqual(pType, kSecPropertyTypeString)) {
            CFStringAppend(line, (CFStringRef)value);
        } else if (CFEqual(pType, kSecPropertyTypeDate)) {
            CFDateRef date = (CFDateRef)value;
            CFLocaleRef lc = CFLocaleCopyCurrent();
            CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc, kCFDateFormatterMediumStyle, kCFDateFormatterLongStyle);
            CFStringRef ds;
            if (df) {
                CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
                CFDateFormatterSetProperty(df, kCFDateFormatterTimeZone, tz);
                CFRelease(tz);
                ds = CFDateFormatterCreateStringWithDate(NULL, df, date);
                CFRelease(df);
            } else {
                ds = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), CFDateGetAbsoluteTime(date));
            }
            CFStringAppend(line, ds);
            CFRelease(ds);
            CFRelease(lc);
        } else if (CFEqual(pType, kSecPropertyTypeURL)) {
            CFURLRef url = (CFURLRef)value;
            CFStringAppend(line, CFSTR("<"));
            CFStringAppend(line, CFURLGetString(url));
            CFStringAppend(line, CFSTR(">"));
        } else {
            CFStringAppendFormat(line, NULL, CFSTR("*unknown type %@* = %@"),
            pType, value);
        }

		if (!isSection || label)
			print_line(line);
		CFRelease(line);
        if (isSection) {
            printPlist((CFArrayRef)value, indent + 1, maxWidth);
        }
    }
}

static CFIndex maxLabelWidth(CFArrayRef plist, CFIndex indent) {
    CFIndex count = CFArrayGetCount(plist);
    CFIndex ix;
    CFIndex maxWidth = 0;
    for (ix = 0; ix < count ; ++ix) {
        CFDictionaryRef prop = (CFDictionaryRef)CFArrayGetValueAtIndex(plist,
            ix);
        CFStringRef pType = (CFStringRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyType);
        CFStringRef llabel = (CFStringRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyLocalizedLabel);
        CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(prop,
            kSecPropertyKeyValue);

        if (CFEqual(pType, kSecPropertyTypeSection)) {
            CFIndex width = maxLabelWidth((CFArrayRef)value, indent + 1);
            if (width > maxWidth)
                maxWidth = width;
        } else if (llabel) {
            CFIndex width = indent * 4 + CFStringGetLength(llabel);
            if (width > maxWidth)
                maxWidth = width;
        }
    }

    return maxWidth;
}

void print_plist(CFArrayRef plist) {
    if (plist)
        printPlist(plist, 0, maxLabelWidth(plist, 0));
    else
        printf("NULL plist\n");
}

void print_cert(SecCertificateRef cert, bool verbose) {
// TODO: merge these when all SecCertificate APIs are present on both iOS and OS X
#if TARGET_OS_IOS
    CFArrayRef plist;
    if (verbose)
        plist = SecCertificateCopyProperties(cert);
    else {
        CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
        plist = SecCertificateCopySummaryProperties(cert, now);
    }

    CFStringRef subject = SecCertificateCopySubjectString(cert);
    if (subject) {
        print_line(subject);
        CFRelease(subject);
    } else {
        print_line(CFSTR("no subject"));
    }

    print_plist(plist);
    CFRelease(plist);
#else
    CFStringRef certName = NULL;
    OSStatus status = SecCertificateInferLabel(cert, &certName);
    if (certName) {
        print_line(certName);
        CFRelease(certName);
    }
    else {
        fprintf(stdout, "ERROR: unable to read certificate name\n");
    }
#endif
}