MYCertGen.m
author snej@snej.local
Sun Apr 12 22:16:14 2009 -0700 (2009-04-12)
changeset 9 aa5eb3fd6ebf
parent 4 f4709533c816
child 21 2c300b15b381
permissions -rw-r--r--
Doc touch-up
     1 //
     2 //  MYCertGen.m
     3 //  MYCrypto
     4 //
     5 //  Created by Jens Alfke on 4/3/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 //  Derived from ...
    10 
    11 //
    12 //  CertificateGeneration.m
    13 //  Keychain
    14 //
    15 //  Created by Wade Tregaskis on Tue May 27 2003.
    16 //
    17 //  Copyright (c) 2003 - 2007, Wade Tregaskis.  All rights reserved.
    18 //  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    19 //    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    20 //    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    21 //    * Neither the name of Wade Tregaskis nor the names of any other contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    22 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    23 
    24 #import "MYCertGen.h"
    25 #import "MYCrypto_Private.h"
    26 #import "MYIdentity.h"
    27 #import <Security/Security.h>
    28 
    29 
    30 
    31 static CSSM_X509_NAME* createNameList( NSDictionary *name );
    32 static CSSM_X509_TIME* timeForNSDate(NSDate *date);
    33 
    34 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone);
    35 
    36 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data);
    37 
    38 
    39 
    40 NSData* MYCertificateCreateTemplate(const CSSM_X509_NAME *subject, const CSSM_X509_NAME *issuer,
    41                                     NSDate *validFrom, NSDate *validTo,
    42                                     uint32_t serialNumber,
    43                                     const CSSM_X509_EXTENSION **extensions, unsigned nExtensions,
    44                                     MYPublicKey *publicKey,
    45                                     const CSSM_X509_ALGORITHM_IDENTIFIER *signingAlgorithm,
    46                                     CSSM_CL_HANDLE clHandle) 
    47 {
    48     CAssert(subject);
    49     CAssert(issuer);
    50     CAssert(publicKey);
    51     CAssert(signingAlgorithm);
    52     CAssert(clHandle);
    53 
    54     const CSSM_KEY *cssmPubKey = publicKey.cssmKey;
    55     if (cssmPubKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) {
    56         cssmPubKey = publicKey._unwrappedCSSMKey;
    57         if (!cssmPubKey) {
    58             Warn(@"MYCertificateCreateTemplate: unable to unwrap public key %@", publicKey);
    59             return nil;
    60         }
    61     }
    62 
    63     uint32_t numberOfFields = 5; // always requires at least 5 user-supplied fields
    64     if (serialNumber)
    65         ++numberOfFields;
    66     if (validFrom)
    67         ++numberOfFields;
    68     if (validTo)
    69         ++numberOfFields;
    70     numberOfFields += nExtensions;
    71     
    72     CSSM_FIELD fields[numberOfFields];
    73 
    74     // now we fill in the fields appropriately
    75     
    76     uint32_t index = 0;
    77     fields[index].FieldOid = CSSMOID_X509V1Version;
    78     intToDER(2, &(fields[index++].FieldValue));
    79 
    80     if (serialNumber) {
    81         fields[index].FieldOid = CSSMOID_X509V1SerialNumber;
    82         intToDER(serialNumber, &fields[index].FieldValue);
    83         index++;
    84     }
    85 
    86     fields[index].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
    87     fields[index].FieldValue.Data = (uint8_t*)issuer;
    88     fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
    89 
    90     fields[index].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
    91     fields[index].FieldValue.Data = (uint8_t*)subject;
    92     fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
    93 
    94     if (validFrom) {
    95         fields[index].FieldOid = CSSMOID_X509V1ValidityNotBefore;
    96         fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validFrom);
    97         fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
    98     }
    99     
   100     if (validTo) {
   101         fields[index].FieldOid = CSSMOID_X509V1ValidityNotAfter;
   102         fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validTo);
   103         fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
   104     }
   105     
   106     fields[index].FieldOid = CSSMOID_CSSMKeyStruct;
   107     fields[index].FieldValue.Data = (uint8_t*)cssmPubKey;
   108     fields[index++].FieldValue.Length = sizeof(CSSM_KEY);
   109 
   110     fields[index].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
   111     fields[index].FieldValue.Data = (uint8_t*)signingAlgorithm;
   112     fields[index++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
   113     
   114     for (unsigned i=0; i<nExtensions; i++) {
   115         fields[index].FieldOid = extensions[i]->extnId;
   116         fields[index].FieldValue.Data = (uint8_t*)extensions[i];
   117         fields[index++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
   118     }
   119     CAssert(index == numberOfFields);
   120     
   121     CSSM_DATA result = {};
   122     checkcssm(CSSM_CL_CertCreateTemplate(clHandle, numberOfFields, fields, &result),
   123               @"CSSM_CL_CertCreateTemplate");
   124     return NSDataFromDataNoCopy(result, YES);
   125 }
   126 
   127 
   128 NSData* MYCertificateSign(NSData *certificateTemplate, 
   129                           MYPrivateKey *privateKey, 
   130                           CSSM_ALGORITHMS signingAlgorithmID,
   131                           CSSM_CL_HANDLE cssmCLHandle) 
   132 {
   133 
   134     CAssert(certificateTemplate.length);
   135     CAssert(privateKey);
   136         
   137     NSData *signedCertificate = nil;
   138     CSSM_CC_HANDLE ccHandle = [privateKey _createSignatureContext: signingAlgorithmID];
   139     if (ccHandle) {
   140         CSSM_DATA rawCert = {certificateTemplate.length, (void*)certificateTemplate.bytes};
   141         CSSM_DATA signedResult = {};
   142         if (checkcssm(CSSM_CL_CertSign(cssmCLHandle, ccHandle, &rawCert, NULL, 0, &signedResult),
   143                       @"CSSM_CL_CertSign")) {
   144             signedCertificate = NSDataFromDataNoCopy(signedResult, YES);
   145             checkcssm(CSSM_DeleteContext(ccHandle), @"CSSM_DeleteContext");
   146         }
   147     }
   148     return signedCertificate;
   149 }
   150 
   151 
   152 MYCertificate* MYCertificateCreateSelfSigned(MYPrivateKey *privateKey,
   153                                              NSDictionary *attributes )
   154 {
   155     // Extract attributes:
   156     NSMutableDictionary *subject = [[attributes mutableCopy] autorelease];
   157 
   158     unsigned serialNumber = [[attributes objectForKey: @"Serial Number"] unsignedIntValue];
   159     [subject removeObjectForKey: @"Serial Number"];
   160 
   161     NSDate *validFrom = [attributes objectForKey: @"Valid From"];
   162     [subject removeObjectForKey: @"Valid From"];
   163     NSDate *validTo = [attributes objectForKey: @"Valid To"];
   164     [subject removeObjectForKey: @"Valid To"];
   165     
   166     if (!serialNumber)
   167         serialNumber = 1;
   168     if (!validFrom)
   169         validFrom = [NSCalendarDate date];
   170     if (!validTo)
   171         validTo = [validFrom addTimeInterval: 60*60*24*366];
   172     
   173     const CSSM_X509_NAME *subjectStruct = createNameList(subject);
   174 
   175     // Create the key-usage extensions for the cert:    
   176     UInt8 keyUsageBits[2] = {0x00,0xFC};
   177     // that's binary 111111000; see http://tools.ietf.org/html/rfc3280#section-4.2.1.3
   178     CSSM_X509_EXTENSION keyUsage = {
   179         CSSMOID_KeyUsage, 
   180         false,      // non-critical
   181         CSSM_X509_DATAFORMAT_PARSED,
   182         {.parsedValue = &keyUsageBits}
   183     };
   184     
   185     // See http://tools.ietf.org/html/rfc3280#section-4.2.1.13
   186     struct ExtendedUsageList {
   187         UInt32 count;
   188         const CSSM_OID *oids;
   189     };
   190     CSSM_OID usageOids[3] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth, CSSMOID_ExtendedKeyUsageAny};
   191     struct ExtendedUsageList extUsageBits = {3, usageOids};
   192     CSSM_X509_EXTENSION extendedKeyUsage = {
   193         CSSMOID_ExtendedKeyUsage,
   194         false,      // non-critical
   195         CSSM_X509_DATAFORMAT_PARSED,
   196         {.parsedValue = &extUsageBits}
   197     };
   198     
   199     const CSSM_X509_EXTENSION* extensions[2] = {&keyUsage, &extendedKeyUsage};
   200     
   201     CSSM_X509_ALGORITHM_IDENTIFIER algorithmID = {.algorithm=CSSMOID_RSA};
   202     CSSM_CL_HANDLE cssmCLHandle = getCLHandle();
   203 
   204     // Now create the certificate request and sign it:
   205     NSData *template, *signedCertificate = nil;
   206     
   207     template = MYCertificateCreateTemplate(subjectStruct, subjectStruct, // issuer==subject (self-signed) 
   208                                            validFrom, validTo, 
   209                                            serialNumber, 
   210                                            extensions, 2,
   211                                            privateKey.publicKey, 
   212                                            &algorithmID, 
   213                                            cssmCLHandle);
   214     if (!template)
   215         return nil;
   216     
   217     signedCertificate = MYCertificateSign(template, 
   218                                           privateKey, 
   219                                           CSSM_ALGID_SHA1WithRSA, 
   220                                           cssmCLHandle);
   221     if (!signedCertificate)
   222         return nil;
   223     
   224     return [[[MYCertificate alloc] initWithCertificateData: signedCertificate 
   225                                                       type: CSSM_CERT_UNKNOWN 
   226                                                   encoding: CSSM_CERT_ENCODING_UNKNOWN] autorelease];
   227 }
   228 
   229 
   230 
   231 MYIdentity* MYIdentityCreateSelfSigned(MYPrivateKey *privateKey,
   232                                        NSDictionary *attributes )
   233 {
   234     MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey, attributes);
   235     if (!cert)
   236         return nil;
   237     if (![privateKey.keychain addCertificate: cert])
   238         return nil;
   239     MYIdentity *identity = [[[MYIdentity alloc] initWithCertificateRef: cert.certificateRef] autorelease];
   240     CAssert(identity);
   241     return identity;
   242 }
   243 
   244 
   245 
   246 #pragma mark -
   247 #pragma mark HELPER FUNCTIONS:
   248 
   249 
   250 static void* mallocAutoreleased( size_t size ) {
   251     NSMutableData *data = [NSMutableData dataWithLength: size];
   252     return data.mutableBytes;
   253 }
   254 
   255 #define callocAutoreleasedArray(TYPE,N)  (TYPE*)mallocAutoreleased( sizeof(TYPE) * (N) )
   256 
   257 
   258 /*
   259     CSSM_X509_NAME:
   260         uint32 numberOfRDNs;
   261         CSSM_X509_RDN_PTR RelativeDistinguishedName:
   262             uint32 numberOfPairs;
   263             CSSM_X509_TYPE_VALUE_PAIR_PTR AttributeTypeAndValue:
   264                 CSSM_OID type;
   265                 CSSM_BER_TAG valueType; // The Tag to be used when this value is BER encoded 
   266                 CSSM_DATA value;
   267 */
   268 
   269 
   270 static CSSM_X509_NAME* createNameList( NSDictionary *name ) {
   271     static NSArray *sNameKeys;
   272     static CSSM_OID sNameOIDs[7];
   273     if (!sNameKeys) {
   274         sNameKeys = [$array(@"Common Name", @"Surname",  @"Description", @"Name", @"Given Name", 
   275                             @"Email Address", @"Unstructured Name") retain];
   276         sNameOIDs[0] = CSSMOID_CommonName;
   277         sNameOIDs[1] = CSSMOID_Surname;
   278         sNameOIDs[2] = CSSMOID_Description;
   279         sNameOIDs[3] = CSSMOID_Name;
   280         sNameOIDs[4] = CSSMOID_GivenName;
   281         sNameOIDs[5] = CSSMOID_EmailAddress;
   282         sNameOIDs[6] = CSSMOID_UnstructuredName;
   283     }
   284     
   285     unsigned n = name.count;
   286     CAssert(n>0);
   287     CSSM_X509_RDN *rdns = callocAutoreleasedArray(CSSM_X509_RDN, name.count);
   288     CSSM_X509_RDN *rdn = &rdns[0];
   289     CSSM_X509_TYPE_VALUE_PAIR *pairs = callocAutoreleasedArray(CSSM_X509_TYPE_VALUE_PAIR, n);
   290     CSSM_X509_TYPE_VALUE_PAIR *pair = &pairs[0];
   291     for (NSString *key in name) {
   292         NSString *value = [name objectForKey: key];
   293         unsigned index = [sNameKeys indexOfObject: key];
   294         CAssert(index!=NSNotFound, @"X509 name key '%@' not supported'", key);
   295         rdn->numberOfPairs = 1;
   296         rdn->AttributeTypeAndValue = pair;
   297         pair->type = sNameOIDs[index];
   298         pair->valueType = BER_TAG_PRINTABLE_STRING;
   299         pair->value.Data = (void*) value.UTF8String;
   300         pair->value.Length = strlen((char*)pair->value.Data);
   301         rdn++;
   302         pair++;
   303     }
   304     CSSM_X509_NAME *outName = callocAutoreleasedArray(CSSM_X509_NAME,1);
   305     outName->numberOfRDNs = n;
   306     outName->RelativeDistinguishedName = rdns;
   307     return outName;
   308 }
   309 
   310 
   311 #pragma mark -
   312 #pragma mark HELPER FUNCTIONS (from Keychain.framework)
   313 
   314 
   315 static CSSM_X509_TIME* timeForNSDate(NSDate *date) {
   316     CAssert(date);
   317     
   318     NSCalendarDate *dateGMT = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: 
   319                                                             date.timeIntervalSinceReferenceDate];
   320     [dateGMT setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
   321     
   322     /* RFC 2549:
   323      
   324      4.1.2.5.2  GeneralizedTime
   325      
   326      The generalized time type, GeneralizedTime, is a standard ASN.1 type
   327      for variable precision representation of time.  Optionally, the
   328      GeneralizedTime field can include a representation of the time
   329      differential between local and Greenwich Mean Time.
   330      
   331      For the purposes of this profile, GeneralizedTime values MUST be
   332      expressed Greenwich Mean Time (Zulu) and MUST include seconds (i.e.,
   333      times are YYYYMMDDHHMMSSZ), even where the number of seconds is zero.
   334      GeneralizedTime values MUST NOT include fractional seconds. */
   335     
   336     CSSM_X509_TIME *result = mallocAutoreleased(sizeof(CSSM_X509_TIME));
   337     result->timeType = BER_TAG_GENERALIZED_TIME;
   338     result->time.Length = 15;
   339     result->time.Data = mallocAutoreleased(16);
   340     [[dateGMT descriptionWithCalendarFormat:@"%Y%m%d%H%M%SZ"] getCString: (char*)(result->time.Data)
   341                                                                maxLength: 16 
   342                                                                 encoding: NSASCIIStringEncoding];
   343     return result;
   344 }
   345 
   346 
   347 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone) {
   348     if (data.Data)
   349         return [NSData dataWithBytesNoCopy:data.Data length:data.Length freeWhenDone: freeWhenDone];
   350     else
   351         return nil;
   352 }
   353 
   354 
   355 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data) {
   356     CAssert(data);
   357     data->Length = 0;
   358     
   359     if (theInt < 0x100) {
   360         data->Data = (uint8_t*)malloc(1);
   361         
   362         if (NULL != data->Data) {
   363             data->Length = 1;
   364 
   365             data->Data[0] = (unsigned char)(theInt);
   366         }
   367     } else if (theInt < 0x10000) {
   368         data->Data = (uint8_t*)malloc(2);
   369         
   370         if (NULL != data->Data) {
   371             data->Length = 2;
   372 
   373             data->Data[0] = (unsigned char)(theInt >> 8);
   374             data->Data[1] = (unsigned char)(theInt);
   375         }
   376     } else if (theInt < 0x1000000) {
   377         data->Data = (uint8_t*)malloc(3);
   378         
   379         if (NULL != data->Data) {
   380             data->Length = 3;
   381 
   382             data->Data[0] = (unsigned char)(theInt >> 16);
   383             data->Data[1] = (unsigned char)(theInt >> 8);
   384             data->Data[2] = (unsigned char)(theInt);
   385         }
   386     } else {
   387         data->Data = (uint8_t*)malloc(4);
   388         
   389         if (NULL != data->Data) {
   390             data->Length = 4;
   391 
   392             data->Data[0] = (unsigned char)(theInt >> 24);
   393             data->Data[1] = (unsigned char)(theInt >> 16);
   394             data->Data[2] = (unsigned char)(theInt >> 8);
   395             data->Data[3] = (unsigned char)(theInt);
   396         }
   397     }
   398     
   399     return (NULL != data->Data);
   400 }
   401 
   402 
   403 
   404 #pragma mark -
   405 #pragma mark HELPER FUNCTIONS (from Apple's source code):
   406 
   407 
   408 // From Apple's cuCdsaUtils.cpp, in libsecurity_cdsa_utils:
   409 
   410 
   411 /*
   412  * Standard app-level memory functions required by CDSA.
   413  */
   414 static void * cuAppMalloc (CSSM_SIZE size, void *allocRef)             {return( malloc(size) );}
   415 static void   cuAppFree (void *mem_ptr, void *allocRef)                {free(mem_ptr);}
   416 static void * cuAppRealloc (void *ptr, CSSM_SIZE size, void *allocRef) {return( realloc( ptr, size ) );}
   417 static void * cuAppCalloc (uint32 num, CSSM_SIZE size, void *allocRef) {return( calloc( num, size ) );}
   418 
   419 static CSSM_VERSION vers = {2, 0};
   420 static CSSM_API_MEMORY_FUNCS memFuncs = {
   421     cuAppMalloc,
   422     cuAppFree,
   423     cuAppRealloc,
   424     cuAppCalloc,
   425     NULL
   426 };
   427 
   428 static CSSM_CL_HANDLE cuClStartup()
   429 {
   430 	CSSM_CL_HANDLE clHand;
   431 	
   432 	if (!checkcssm(CSSM_ModuleLoad(&gGuidAppleX509CL,
   433                                    CSSM_KEY_HIERARCHY_NONE,
   434                                    NULL,			// eventHandler
   435                                    NULL), @"CSSM_ModuleLoad"))
   436         return 0;
   437     if (!checkcssm(CSSM_ModuleAttach(&gGuidAppleX509CL,
   438                                      &vers,
   439                                      &memFuncs,				// memFuncs
   440                                      0,						// SubserviceID
   441                                      CSSM_SERVICE_CL,		// SubserviceFlags - Where is this used?
   442                                      0,						// AttachFlags
   443                                      CSSM_KEY_HIERARCHY_NONE,
   444                                      NULL,					// FunctionTable
   445                                      0,						// NumFuncTable
   446                                      NULL,					// reserved
   447                                      &clHand), @"CSSM_ModuleAttach"))
   448         return 0;
   449     return clHand;
   450 }
   451         
   452 CSSM_CL_HANDLE getCLHandle() {
   453     static CSSM_CL_HANDLE sCLHandle = 0;
   454     if (!sCLHandle)
   455         sCLHandle = cuClStartup();
   456     return sCLHandle;
   457 }
   458 
   459 
   460 #pragma mark -
   461 #pragma mark TEST CASE:
   462 
   463 
   464 TestCase(MYCertGen) {
   465     CSSM_CL_HANDLE cl = getCLHandle();
   466     Log(@"CSSM_CL_HANDLE = %p", cl);
   467     CAssert(cl);
   468     
   469     Log(@"Generating a key pair...");
   470     MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 2048];
   471     Log(@"Key-pair = { %@, %@ }", privateKey, privateKey.publicKey);
   472     
   473     Log(@"...creating cert...");
   474     
   475     MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey,
   476                                                       $dict(
   477                                                           {@"Common Name", @"waldo"},
   478                                                           {@"Given Name", @"Waldo"},
   479                                                           {@"Surname", @"Widdershins"},
   480                                                           {@"Email Address", @"waldo@example.com"},
   481                                                           {@"Description", @"Just a fictitious person"},
   482                                                       ));
   483     Log(@"Cert = %@", cert);
   484     CAssert(cert);
   485     [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
   486     
   487     Log(@"Cert name = %@", cert.commonName);
   488     Log(@"Cert email = %@", cert.emailAddresses);
   489     Log(@"Cert pub key = %@", cert.publicKey);
   490     CAssertEqual(cert.commonName, @"waldo");
   491     CAssertEqual(cert.emailAddresses, $array(@"waldo@example.com"));
   492     CAssertEqual(cert.publicKey.publicKeyDigest, privateKey.publicKeyDigest);
   493     
   494     CAssert([[MYKeychain defaultKeychain] addCertificate: cert]);
   495     
   496     CAssert([cert setUserTrust: kSecTrustResultProceed]);
   497 }