Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
5 // Created by Jens Alfke on 4/3/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
12 // CertificateGeneration.m
15 // Created by Wade Tregaskis on Tue May 27 2003.
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.
25 #import "MYCrypto_Private.h"
26 #import "MYIdentity.h"
27 #import <Security/Security.h>
31 static CSSM_X509_NAME* createNameList( NSDictionary *name );
32 static CSSM_X509_TIME* timeForNSDate(NSDate *date);
34 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone);
36 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data);
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)
51 CAssert(signingAlgorithm);
54 const CSSM_KEY *cssmPubKey = publicKey.cssmKey;
55 if (cssmPubKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) {
56 cssmPubKey = publicKey._unwrappedCSSMKey;
58 Warn(@"MYCertificateCreateTemplate: unable to unwrap public key %@", publicKey);
63 uint32_t numberOfFields = 5; // always requires at least 5 user-supplied fields
70 numberOfFields += nExtensions;
72 CSSM_FIELD fields[numberOfFields];
74 // now we fill in the fields appropriately
77 fields[index].FieldOid = CSSMOID_X509V1Version;
78 intToDER(2, &(fields[index++].FieldValue));
81 fields[index].FieldOid = CSSMOID_X509V1SerialNumber;
82 intToDER(serialNumber, &fields[index].FieldValue);
86 fields[index].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
87 fields[index].FieldValue.Data = (uint8_t*)issuer;
88 fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
90 fields[index].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
91 fields[index].FieldValue.Data = (uint8_t*)subject;
92 fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
95 fields[index].FieldOid = CSSMOID_X509V1ValidityNotBefore;
96 fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validFrom);
97 fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
101 fields[index].FieldOid = CSSMOID_X509V1ValidityNotAfter;
102 fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validTo);
103 fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
106 fields[index].FieldOid = CSSMOID_CSSMKeyStruct;
107 fields[index].FieldValue.Data = (uint8_t*)cssmPubKey;
108 fields[index++].FieldValue.Length = sizeof(CSSM_KEY);
110 fields[index].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
111 fields[index].FieldValue.Data = (uint8_t*)signingAlgorithm;
112 fields[index++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
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);
119 CAssert(index == numberOfFields);
121 CSSM_DATA result = {};
122 checkcssm(CSSM_CL_CertCreateTemplate(clHandle, numberOfFields, fields, &result),
123 @"CSSM_CL_CertCreateTemplate");
124 return NSDataFromDataNoCopy(result, YES);
128 NSData* MYCertificateSign(NSData *certificateTemplate,
129 MYPrivateKey *privateKey,
130 CSSM_ALGORITHMS signingAlgorithmID,
131 CSSM_CL_HANDLE cssmCLHandle)
134 CAssert(certificateTemplate.length);
137 NSData *signedCertificate = nil;
138 CSSM_CC_HANDLE ccHandle = [privateKey _createSignatureContext: signingAlgorithmID];
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");
148 return signedCertificate;
152 MYCertificate* MYCertificateCreateSelfSigned(MYPrivateKey *privateKey,
153 NSDictionary *attributes )
155 // Extract attributes:
156 NSMutableDictionary *subject = [[attributes mutableCopy] autorelease];
158 unsigned serialNumber = [[attributes objectForKey: @"Serial Number"] unsignedIntValue];
159 [subject removeObjectForKey: @"Serial Number"];
161 NSDate *validFrom = [attributes objectForKey: @"Valid From"];
162 [subject removeObjectForKey: @"Valid From"];
163 NSDate *validTo = [attributes objectForKey: @"Valid To"];
164 [subject removeObjectForKey: @"Valid To"];
169 validFrom = [NSCalendarDate date];
171 validTo = [validFrom addTimeInterval: 60*60*24*366];
173 const CSSM_X509_NAME *subjectStruct = createNameList(subject);
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 = {
180 false, // non-critical
181 CSSM_X509_DATAFORMAT_PARSED,
182 {.parsedValue = &keyUsageBits}
185 // See http://tools.ietf.org/html/rfc3280#section-4.2.1.13
186 struct ExtendedUsageList {
188 const CSSM_OID *oids;
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}
199 const CSSM_X509_EXTENSION* extensions[2] = {&keyUsage, &extendedKeyUsage};
201 CSSM_X509_ALGORITHM_IDENTIFIER algorithmID = {.algorithm=CSSMOID_RSA};
202 CSSM_CL_HANDLE cssmCLHandle = getCLHandle();
204 // Now create the certificate request and sign it:
205 NSData *template, *signedCertificate = nil;
207 template = MYCertificateCreateTemplate(subjectStruct, subjectStruct, // issuer==subject (self-signed)
211 privateKey.publicKey,
217 signedCertificate = MYCertificateSign(template,
219 CSSM_ALGID_SHA1WithRSA,
221 if (!signedCertificate)
224 return [[[MYCertificate alloc] initWithCertificateData: signedCertificate
225 type: CSSM_CERT_UNKNOWN
226 encoding: CSSM_CERT_ENCODING_UNKNOWN] autorelease];
231 MYIdentity* MYIdentityCreateSelfSigned(MYPrivateKey *privateKey,
232 NSDictionary *attributes )
234 MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey, attributes);
237 if (![privateKey.keychain addCertificate: cert])
239 MYIdentity *identity = [[[MYIdentity alloc] initWithCertificateRef: cert.certificateRef] autorelease];
247 #pragma mark HELPER FUNCTIONS:
250 static void* mallocAutoreleased( size_t size ) {
251 NSMutableData *data = [NSMutableData dataWithLength: size];
252 return data.mutableBytes;
255 #define callocAutoreleasedArray(TYPE,N) (TYPE*)mallocAutoreleased( sizeof(TYPE) * (N) )
261 CSSM_X509_RDN_PTR RelativeDistinguishedName:
262 uint32 numberOfPairs;
263 CSSM_X509_TYPE_VALUE_PAIR_PTR AttributeTypeAndValue:
265 CSSM_BER_TAG valueType; // The Tag to be used when this value is BER encoded
270 static CSSM_X509_NAME* createNameList( NSDictionary *name ) {
271 static NSArray *sNameKeys;
272 static CSSM_OID sNameOIDs[7];
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;
285 unsigned n = name.count;
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);
304 CSSM_X509_NAME *outName = callocAutoreleasedArray(CSSM_X509_NAME,1);
305 outName->numberOfRDNs = n;
306 outName->RelativeDistinguishedName = rdns;
312 #pragma mark HELPER FUNCTIONS (from Keychain.framework)
315 static CSSM_X509_TIME* timeForNSDate(NSDate *date) {
318 NSCalendarDate *dateGMT = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:
319 date.timeIntervalSinceReferenceDate];
320 [dateGMT setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
324 4.1.2.5.2 GeneralizedTime
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.
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. */
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)
342 encoding: NSASCIIStringEncoding];
347 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone) {
349 return [NSData dataWithBytesNoCopy:data.Data length:data.Length freeWhenDone: freeWhenDone];
355 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data) {
359 if (theInt < 0x100) {
360 data->Data = (uint8_t*)malloc(1);
362 if (NULL != data->Data) {
365 data->Data[0] = (unsigned char)(theInt);
367 } else if (theInt < 0x10000) {
368 data->Data = (uint8_t*)malloc(2);
370 if (NULL != data->Data) {
373 data->Data[0] = (unsigned char)(theInt >> 8);
374 data->Data[1] = (unsigned char)(theInt);
376 } else if (theInt < 0x1000000) {
377 data->Data = (uint8_t*)malloc(3);
379 if (NULL != data->Data) {
382 data->Data[0] = (unsigned char)(theInt >> 16);
383 data->Data[1] = (unsigned char)(theInt >> 8);
384 data->Data[2] = (unsigned char)(theInt);
387 data->Data = (uint8_t*)malloc(4);
389 if (NULL != data->Data) {
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);
399 return (NULL != data->Data);
405 #pragma mark HELPER FUNCTIONS (from Apple's source code):
408 // From Apple's cuCdsaUtils.cpp, in libsecurity_cdsa_utils:
412 * Standard app-level memory functions required by CDSA.
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 ) );}
419 static CSSM_VERSION vers = {2, 0};
420 static CSSM_API_MEMORY_FUNCS memFuncs = {
428 static CSSM_CL_HANDLE cuClStartup()
430 CSSM_CL_HANDLE clHand;
432 if (!checkcssm(CSSM_ModuleLoad(&gGuidAppleX509CL,
433 CSSM_KEY_HIERARCHY_NONE,
434 NULL, // eventHandler
435 NULL), @"CSSM_ModuleLoad"))
437 if (!checkcssm(CSSM_ModuleAttach(&gGuidAppleX509CL,
439 &memFuncs, // memFuncs
441 CSSM_SERVICE_CL, // SubserviceFlags - Where is this used?
443 CSSM_KEY_HIERARCHY_NONE,
444 NULL, // FunctionTable
447 &clHand), @"CSSM_ModuleAttach"))
452 CSSM_CL_HANDLE getCLHandle() {
453 static CSSM_CL_HANDLE sCLHandle = 0;
455 sCLHandle = cuClStartup();
461 #pragma mark TEST CASE:
464 TestCase(MYCertGen) {
465 CSSM_CL_HANDLE cl = getCLHandle();
466 Log(@"CSSM_CL_HANDLE = %p", cl);
469 Log(@"Generating a key pair...");
470 MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 2048];
471 Log(@"Key-pair = { %@, %@ }", privateKey, privateKey.publicKey);
473 Log(@"...creating cert...");
475 MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey,
477 {@"Common Name", @"waldo"},
478 {@"Given Name", @"Waldo"},
479 {@"Surname", @"Widdershins"},
480 {@"Email Address", @"waldo@example.com"},
481 {@"Description", @"Just a fictitious person"},
483 Log(@"Cert = %@", cert);
485 [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
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);
494 CAssert([[MYKeychain defaultKeychain] addCertificate: cert]);
496 CAssert([cert setUserTrust: kSecTrustResultProceed]);