A snapshot taken during the long, agonizing crawl toward getting everything running on iPhone.
5 // Created by Jens Alfke on 4/3/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 // NOTE: This module has been replaced by MYCertificateInfo, which isn't dependent on
10 // CSSM APIs that aren't available on iPhone.
15 // CertificateGeneration.m
18 // Created by Wade Tregaskis on Tue May 27 2003.
20 // Copyright (c) 2003 - 2007, Wade Tregaskis. All rights reserved.
21 // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
22 // * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
23 // * 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.
24 // * 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.
25 // 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.
28 #import "MYCrypto_Private.h"
29 #import "MYIdentity.h"
30 #import <Security/Security.h>
34 static CSSM_X509_NAME* createNameList( NSDictionary *name );
35 static CSSM_X509_TIME* timeForNSDate(NSDate *date);
37 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone);
39 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data);
43 NSData* MYCertificateCreateTemplate(const CSSM_X509_NAME *subject, const CSSM_X509_NAME *issuer,
44 NSDate *validFrom, NSDate *validTo,
45 uint32_t serialNumber,
46 const CSSM_X509_EXTENSION **extensions, unsigned nExtensions,
47 MYPublicKey *publicKey,
48 const CSSM_X509_ALGORITHM_IDENTIFIER *signingAlgorithm,
49 CSSM_CL_HANDLE clHandle)
54 CAssert(signingAlgorithm);
57 const CSSM_KEY *cssmPubKey = publicKey.cssmKey;
58 if (cssmPubKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) {
59 cssmPubKey = publicKey._unwrappedCSSMKey;
61 Warn(@"MYCertificateCreateTemplate: unable to unwrap public key %@", publicKey);
66 uint32_t numberOfFields = 5; // always requires at least 5 user-supplied fields
73 numberOfFields += nExtensions;
75 CSSM_FIELD fields[numberOfFields];
77 // now we fill in the fields appropriately
80 fields[index].FieldOid = CSSMOID_X509V1Version;
81 intToDER(2, &(fields[index++].FieldValue));
84 fields[index].FieldOid = CSSMOID_X509V1SerialNumber;
85 intToDER(serialNumber, &fields[index].FieldValue);
89 fields[index].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
90 fields[index].FieldValue.Data = (uint8_t*)issuer;
91 fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
93 fields[index].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
94 fields[index].FieldValue.Data = (uint8_t*)subject;
95 fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
98 fields[index].FieldOid = CSSMOID_X509V1ValidityNotBefore;
99 fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validFrom);
100 fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
104 fields[index].FieldOid = CSSMOID_X509V1ValidityNotAfter;
105 fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validTo);
106 fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
109 fields[index].FieldOid = CSSMOID_CSSMKeyStruct;
110 fields[index].FieldValue.Data = (uint8_t*)cssmPubKey;
111 fields[index++].FieldValue.Length = sizeof(CSSM_KEY);
113 fields[index].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
114 fields[index].FieldValue.Data = (uint8_t*)signingAlgorithm;
115 fields[index++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
117 for (unsigned i=0; i<nExtensions; i++) {
118 fields[index].FieldOid = extensions[i]->extnId;
119 fields[index].FieldValue.Data = (uint8_t*)extensions[i];
120 fields[index++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
122 CAssert(index == numberOfFields);
124 CSSM_DATA result = {};
125 checkcssm(CSSM_CL_CertCreateTemplate(clHandle, numberOfFields, fields, &result),
126 @"CSSM_CL_CertCreateTemplate");
127 return NSDataFromDataNoCopy(result, YES);
131 NSData* MYCertificateSign(NSData *certificateTemplate,
132 MYPrivateKey *privateKey,
133 CSSM_ALGORITHMS signingAlgorithmID,
134 CSSM_CL_HANDLE cssmCLHandle)
137 CAssert(certificateTemplate.length);
140 NSData *signedCertificate = nil;
141 CSSM_CC_HANDLE ccHandle = [privateKey _createSignatureContext: signingAlgorithmID];
143 CSSM_DATA rawCert = {certificateTemplate.length, (void*)certificateTemplate.bytes};
144 CSSM_DATA signedResult = {};
145 if (checkcssm(CSSM_CL_CertSign(cssmCLHandle, ccHandle, &rawCert, NULL, 0, &signedResult),
146 @"CSSM_CL_CertSign")) {
147 signedCertificate = NSDataFromDataNoCopy(signedResult, YES);
148 checkcssm(CSSM_DeleteContext(ccHandle), @"CSSM_DeleteContext");
151 return signedCertificate;
155 MYCertificate* MYCertificateCreateSelfSigned(MYPrivateKey *privateKey,
156 NSDictionary *attributes )
158 // Extract attributes:
159 NSMutableDictionary *subject = [[attributes mutableCopy] autorelease];
161 unsigned serialNumber = [[attributes objectForKey: @"Serial Number"] unsignedIntValue];
162 [subject removeObjectForKey: @"Serial Number"];
164 NSDate *validFrom = [attributes objectForKey: @"Valid From"];
165 [subject removeObjectForKey: @"Valid From"];
166 NSDate *validTo = [attributes objectForKey: @"Valid To"];
167 [subject removeObjectForKey: @"Valid To"];
172 validFrom = [NSCalendarDate date];
174 validTo = [validFrom addTimeInterval: 60*60*24*366];
176 const CSSM_X509_NAME *subjectStruct = createNameList(subject);
178 // Create the key-usage extensions for the cert:
179 UInt8 keyUsageBits[2] = {0x00,0xFC};
180 // that's binary 111111000; see http://tools.ietf.org/html/rfc3280#section-4.2.1.3
181 CSSM_X509_EXTENSION keyUsage = {
183 false, // non-critical
184 CSSM_X509_DATAFORMAT_PARSED,
185 {.parsedValue = &keyUsageBits}
188 // See http://tools.ietf.org/html/rfc3280#section-4.2.1.13
189 struct ExtendedUsageList {
191 const CSSM_OID *oids;
193 CSSM_OID usageOids[3] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth, CSSMOID_ExtendedKeyUsageAny};
194 struct ExtendedUsageList extUsageBits = {3, usageOids};
195 CSSM_X509_EXTENSION extendedKeyUsage = {
196 CSSMOID_ExtendedKeyUsage,
197 false, // non-critical
198 CSSM_X509_DATAFORMAT_PARSED,
199 {.parsedValue = &extUsageBits}
202 const CSSM_X509_EXTENSION* extensions[2] = {&keyUsage, &extendedKeyUsage};
204 CSSM_X509_ALGORITHM_IDENTIFIER algorithmID = {.algorithm=CSSMOID_RSA};
205 CSSM_CL_HANDLE cssmCLHandle = getCLHandle();
207 // Now create the certificate request and sign it:
208 NSData *template, *signedCertificate = nil;
210 template = MYCertificateCreateTemplate(subjectStruct, subjectStruct, // issuer==subject (self-signed)
214 privateKey.publicKey,
220 signedCertificate = MYCertificateSign(template,
222 CSSM_ALGID_SHA1WithRSA,
224 if (!signedCertificate)
227 return [[[MYCertificate alloc] initWithCertificateData: signedCertificate
228 type: CSSM_CERT_UNKNOWN
229 encoding: CSSM_CERT_ENCODING_UNKNOWN] autorelease];
234 MYIdentity* MYIdentityCreateSelfSigned(MYPrivateKey *privateKey,
235 NSDictionary *attributes )
237 MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey, attributes);
240 if (![privateKey.keychain addCertificate: cert])
242 MYIdentity *identity = [[[MYIdentity alloc] initWithCertificateRef: cert.certificateRef] autorelease];
250 #pragma mark HELPER FUNCTIONS:
253 static void* mallocAutoreleased( size_t size ) {
254 NSMutableData *data = [NSMutableData dataWithLength: size];
255 return data.mutableBytes;
258 #define callocAutoreleasedArray(TYPE,N) (TYPE*)mallocAutoreleased( sizeof(TYPE) * (N) )
264 CSSM_X509_RDN_PTR RelativeDistinguishedName:
265 uint32 numberOfPairs;
266 CSSM_X509_TYPE_VALUE_PAIR_PTR AttributeTypeAndValue:
268 CSSM_BER_TAG valueType; // The Tag to be used when this value is BER encoded
273 static CSSM_X509_NAME* createNameList( NSDictionary *name ) {
274 static NSArray *sNameKeys;
275 static CSSM_OID sNameOIDs[7];
277 sNameKeys = [$array(@"Common Name", @"Surname", @"Description", @"Name", @"Given Name",
278 @"Email Address", @"Unstructured Name") retain];
279 sNameOIDs[0] = CSSMOID_CommonName;
280 sNameOIDs[1] = CSSMOID_Surname;
281 sNameOIDs[2] = CSSMOID_Description;
282 sNameOIDs[3] = CSSMOID_Name;
283 sNameOIDs[4] = CSSMOID_GivenName;
284 sNameOIDs[5] = CSSMOID_EmailAddress;
285 sNameOIDs[6] = CSSMOID_UnstructuredName;
288 unsigned n = name.count;
290 CSSM_X509_RDN *rdns = callocAutoreleasedArray(CSSM_X509_RDN, name.count);
291 CSSM_X509_RDN *rdn = &rdns[0];
292 CSSM_X509_TYPE_VALUE_PAIR *pairs = callocAutoreleasedArray(CSSM_X509_TYPE_VALUE_PAIR, n);
293 CSSM_X509_TYPE_VALUE_PAIR *pair = &pairs[0];
294 for (NSString *key in name) {
295 NSString *value = [name objectForKey: key];
296 unsigned index = [sNameKeys indexOfObject: key];
297 CAssert(index!=NSNotFound, @"X509 name key '%@' not supported'", key);
298 rdn->numberOfPairs = 1;
299 rdn->AttributeTypeAndValue = pair;
300 pair->type = sNameOIDs[index];
301 pair->valueType = BER_TAG_PRINTABLE_STRING;
302 pair->value.Data = (void*) value.UTF8String;
303 pair->value.Length = strlen((char*)pair->value.Data);
307 CSSM_X509_NAME *outName = callocAutoreleasedArray(CSSM_X509_NAME,1);
308 outName->numberOfRDNs = n;
309 outName->RelativeDistinguishedName = rdns;
315 #pragma mark HELPER FUNCTIONS (from Keychain.framework)
318 static CSSM_X509_TIME* timeForNSDate(NSDate *date) {
321 NSCalendarDate *dateGMT = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:
322 date.timeIntervalSinceReferenceDate];
323 [dateGMT setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
327 4.1.2.5.2 GeneralizedTime
329 The generalized time type, GeneralizedTime, is a standard ASN.1 type
330 for variable precision representation of time. Optionally, the
331 GeneralizedTime field can include a representation of the time
332 differential between local and Greenwich Mean Time.
334 For the purposes of this profile, GeneralizedTime values MUST be
335 expressed Greenwich Mean Time (Zulu) and MUST include seconds (i.e.,
336 times are YYYYMMDDHHMMSSZ), even where the number of seconds is zero.
337 GeneralizedTime values MUST NOT include fractional seconds. */
339 CSSM_X509_TIME *result = mallocAutoreleased(sizeof(CSSM_X509_TIME));
340 result->timeType = BER_TAG_GENERALIZED_TIME;
341 result->time.Length = 15;
342 result->time.Data = mallocAutoreleased(16);
343 [[dateGMT descriptionWithCalendarFormat:@"%Y%m%d%H%M%SZ"] getCString: (char*)(result->time.Data)
345 encoding: NSASCIIStringEncoding];
350 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone) {
352 return [NSData dataWithBytesNoCopy:data.Data length:data.Length freeWhenDone: freeWhenDone];
358 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data) {
362 if (theInt < 0x100) {
363 data->Data = (uint8_t*)malloc(1);
365 if (NULL != data->Data) {
368 data->Data[0] = (unsigned char)(theInt);
370 } else if (theInt < 0x10000) {
371 data->Data = (uint8_t*)malloc(2);
373 if (NULL != data->Data) {
376 data->Data[0] = (unsigned char)(theInt >> 8);
377 data->Data[1] = (unsigned char)(theInt);
379 } else if (theInt < 0x1000000) {
380 data->Data = (uint8_t*)malloc(3);
382 if (NULL != data->Data) {
385 data->Data[0] = (unsigned char)(theInt >> 16);
386 data->Data[1] = (unsigned char)(theInt >> 8);
387 data->Data[2] = (unsigned char)(theInt);
390 data->Data = (uint8_t*)malloc(4);
392 if (NULL != data->Data) {
395 data->Data[0] = (unsigned char)(theInt >> 24);
396 data->Data[1] = (unsigned char)(theInt >> 16);
397 data->Data[2] = (unsigned char)(theInt >> 8);
398 data->Data[3] = (unsigned char)(theInt);
402 return (NULL != data->Data);
408 #pragma mark HELPER FUNCTIONS (from Apple's source code):
411 // From Apple's cuCdsaUtils.cpp, in libsecurity_cdsa_utils:
415 * Standard app-level memory functions required by CDSA.
417 static void * cuAppMalloc (CSSM_SIZE size, void *allocRef) {return( malloc(size) );}
418 static void cuAppFree (void *mem_ptr, void *allocRef) {free(mem_ptr);}
419 static void * cuAppRealloc (void *ptr, CSSM_SIZE size, void *allocRef) {return( realloc( ptr, size ) );}
420 static void * cuAppCalloc (uint32 num, CSSM_SIZE size, void *allocRef) {return( calloc( num, size ) );}
422 static CSSM_VERSION vers = {2, 0};
423 static CSSM_API_MEMORY_FUNCS memFuncs = {
431 static CSSM_CL_HANDLE cuClStartup()
433 CSSM_CL_HANDLE clHand;
435 if (!checkcssm(CSSM_ModuleLoad(&gGuidAppleX509CL,
436 CSSM_KEY_HIERARCHY_NONE,
437 NULL, // eventHandler
438 NULL), @"CSSM_ModuleLoad"))
440 if (!checkcssm(CSSM_ModuleAttach(&gGuidAppleX509CL,
442 &memFuncs, // memFuncs
444 CSSM_SERVICE_CL, // SubserviceFlags - Where is this used?
446 CSSM_KEY_HIERARCHY_NONE,
447 NULL, // FunctionTable
450 &clHand), @"CSSM_ModuleAttach"))
455 CSSM_CL_HANDLE getCLHandle() {
456 static CSSM_CL_HANDLE sCLHandle = 0;
458 sCLHandle = cuClStartup();
464 #pragma mark TEST CASE:
467 TestCase(MYCertGen) {
468 CSSM_CL_HANDLE cl = getCLHandle();
469 Log(@"CSSM_CL_HANDLE = %p", cl);
472 Log(@"Generating a key pair...");
473 MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 2048];
474 Log(@"Key-pair = { %@, %@ }", privateKey, privateKey.publicKey);
476 Log(@"...creating cert...");
478 MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey,
480 {@"Common Name", @"waldo"},
481 {@"Given Name", @"Waldo"},
482 {@"Surname", @"Widdershins"},
483 {@"Email Address", @"waldo@example.com"},
484 {@"Description", @"Just a fictitious person"},
486 Log(@"Cert = %@", cert);
488 [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
490 Log(@"Cert name = %@", cert.commonName);
491 Log(@"Cert email = %@", cert.emailAddresses);
492 Log(@"Cert pub key = %@", cert.publicKey);
493 CAssertEqual(cert.commonName, @"waldo");
494 CAssertEqual(cert.emailAddresses, $array(@"waldo@example.com"));
495 CAssertEqual(cert.publicKey.publicKeyDigest, privateKey.publicKeyDigest);
497 CAssert([[MYKeychain defaultKeychain] addCertificate: cert]);
499 CAssert([cert setUserTrust: kSecTrustResultProceed]);
505 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
507 Redistribution and use in source and binary forms, with or without modification, are permitted
508 provided that the following conditions are met:
510 * Redistributions of source code must retain the above copyright notice, this list of conditions
511 and the following disclaimer.
512 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
513 and the following disclaimer in the documentation and/or other materials provided with the
516 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
517 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
518 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
519 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
520 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
521 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
522 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
523 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.