2 * WebNewKeyGeneration.c
5 * Created by Chris Blumenberg on Mon Aug 23 2004.
6 * Copyright (c) 2003 Apple Computer. All rights reserved.
10 #import <WebKit/WebNewKeyGeneration.h>
12 #ifdef USE_NEW_KEY_GENERATION
14 #import <WebKit/WebAssertions.h>
16 #import <Security/keyTemplates.h>
17 #import <Security/SecKeyPriv.h> /* Security.framework SPI */
19 #import <security_cdsa_utils/cuEnc64.h>
21 /* hard coded params, some of which may come from the user in real life */
22 #define GNR_KEY_ALG CSSM_ALGID_RSA
23 #define GNR_SIG_ALG CSSM_ALGID_MD5WithRSA
24 #define GNR_SIG_ALGOID CSSMOID_MD5WithRSA
26 const SecAsn1Template NetscapeCertSequenceTemplate[] = {
28 0, NULL, sizeof(NetscapeCertSequence) },
30 offsetof(NetscapeCertSequence, contentType), 0, 0 },
31 { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
32 SEC_ASN1_CONTEXT_SPECIFIC | 0 ,
33 offsetof(NetscapeCertSequence, certs),
34 kSecAsn1SequenceOfAnyTemplate, 0 },
38 const SecAsn1Template PublicKeyAndChallengeTemplate[] = {
40 0, NULL, sizeof(PublicKeyAndChallenge) },
42 offsetof(PublicKeyAndChallenge, spki),
43 kSecAsn1SubjectPublicKeyInfoTemplate, 0},
45 offsetof(PublicKeyAndChallenge, challenge),
46 kSecAsn1IA5StringTemplate, 0 },
50 const SecAsn1Template SignedPublicKeyAndChallengeTemplate[] = {
52 0, NULL, sizeof(SignedPublicKeyAndChallenge) },
54 offsetof(SignedPublicKeyAndChallenge, pubKeyAndChallenge),
55 PublicKeyAndChallengeTemplate, 0 },
57 offsetof(SignedPublicKeyAndChallenge, algId),
58 kSecAsn1AlgorithmIDTemplate, 0 },
59 { SEC_ASN1_BIT_STRING,
60 offsetof(SignedPublicKeyAndChallenge, signature), 0, 0 },
64 void gnrNullAlgParams(CSSM_X509_ALGORITHM_IDENTIFIER *algId);
65 CSSM_RETURN gnrSign(CSSM_CSP_HANDLE cspHand,
66 const CSSM_DATA *plainText,
68 CSSM_ALGORITHMS sigAlg, // e.g., CSSM_ALGID_SHA1WithRSA
70 unsigned nssArraySize(const void **array);
71 bool addCertificateToKeychainFromData(const unsigned char *certData,
76 * Given a context specified via a CSSM_CC_HANDLE, add a new
77 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
78 * AttributeLength, and an untyped pointer.
80 * This is currently used to add a second CSSM_KEY attribute when performing
81 * ops with algorithm CSSM_ALGID_FEED and CSSM_ALGID_FEECFILE.
83 static CSSM_RETURN gnrAddContextAttribute(CSSM_CC_HANDLE CCHandle,
85 uint32 AttributeLength,
86 const void *AttributePtr)
88 CSSM_CONTEXT_ATTRIBUTE newAttr;
91 newAttr.AttributeType = AttributeType;
92 newAttr.AttributeLength = AttributeLength;
93 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
94 crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
96 ERROR("CSSM_UpdateContextAttributes", crtn);
102 * Given a public key as a SecKeyRef, obtain the key material in
103 * SubjectPublicKeyInfo format. This entails a NULL wrap to format
104 * in CSSM_KEYBLOB_RAW_FORMAT_X509 form. Caller must eventually
105 * free the returned key via CSSM_FreeKey().
107 static OSStatus gnrGetSubjPubKey(
108 CSSM_CSP_HANDLE cspHand,
110 CSSM_KEY_PTR subjPubKey) // RETURNED
112 CSSM_CC_HANDLE ccHand;
114 CSSM_ACCESS_CREDENTIALS creds;
115 const CSSM_KEY *refPubKey;
118 /* Get public key in CSSM form */
119 ortn = SecKeyGetCSSMKey(secKey, &refPubKey);
121 ERROR("SecKeyGetCSSMKey", ortn);
125 /* NULL wrap via CSPDL */
126 memset(subjPubKey, 0, sizeof(CSSM_KEY));
127 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
128 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
131 &creds, // passPhrase
132 NULL, // wrapping key
134 CSSM_PADDING_NONE, // Padding
138 ERROR("gnrGetSubjPubKey CSSM_CSP_CreateSymmetricContext",
144 * Specify X509 format' that is NOT the default for RSA (PKCS1 is)
146 crtn = gnrAddContextAttribute(ccHand,
147 CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
149 (void *)CSSM_KEYBLOB_RAW_FORMAT_X509);
151 ERROR("gnrAddContextAttribute", crtn);
155 crtn = CSSM_WrapKey(ccHand,
158 NULL, // DescriptiveData
161 ERROR("CSSM_WrapKey", crtn);
164 CSSM_DeleteContext(ccHand);
169 * Set up a encoded NULL for CSSM_X509_ALGORITHM_IDENTIFIER.parameters.
171 void gnrNullAlgParams(CSSM_X509_ALGORITHM_IDENTIFIER *algId)
173 static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 };
174 algId->parameters.Data = (uint8 *)encNull;
175 algId->parameters.Length = 2;
179 * Sign specified plaintext. Caller must free signature data via
182 CSSM_RETURN gnrSign(CSSM_CSP_HANDLE cspHand,
183 const CSSM_DATA *plainText,
185 CSSM_ALGORITHMS sigAlg, // e.g., CSSM_ALGID_SHA1WithRSA
186 CSSM_DATA *sig) // allocated by CSP and RETURNED
188 CSSM_CC_HANDLE ccHand;
190 const CSSM_KEY *refPrivKey;
192 const CSSM_ACCESS_CREDENTIALS *creds;
194 /* Get private key in CSSM form */
195 ortn = SecKeyGetCSSMKey(privKey, &refPrivKey);
197 ERROR("SecKeyGetCSSMKey", ortn);
201 /* Get appropriate access credentials */
202 ortn = SecKeyGetCredentials(privKey,
203 CSSM_ACL_AUTHORIZATION_SIGN,
204 kSecCredentialTypeDefault,
207 ERROR("SecKeyGetCredentials", ortn);
211 /* cook up signature context */
212 crtn = CSSM_CSP_CreateSignatureContext(cspHand,
218 ERROR("CSSM_CSP_CreateSignatureContext", ortn);
225 crtn = CSSM_SignData(ccHand,
231 ERROR("CSSM_SignData", ortn);
233 CSSM_DeleteContext(ccHand);
238 * Free data mallocd on app's behalf by a CSSM module.
240 static void gnrFreeCssmData(
244 CSSM_API_MEMORY_FUNCS memFuncs;
245 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(modHand, &memFuncs);
247 ERROR("CSSM_GetAPIMemoryFunctions", crtn);
248 /* oh well, leak and continue */
251 memFuncs.free_func(cdata->Data, memFuncs.AllocRef);
256 unsigned nssArraySize(const void **array)
267 CFStringRef signedPublicKeyAndChallengeString(unsigned keySize, CFStringRef challenge, CFStringRef keyDescription)
271 SecKeyRef pubKey = NULL;
272 SecKeyRef privKey = NULL;
273 CSSM_KEY subjectPubKey;
274 bool freeSubjPubKey = false;
275 CSSM_CSP_HANDLE cspHand;
276 SecAsn1CoderRef coder = NULL;
277 SignedPublicKeyAndChallenge spkc;
278 PublicKeyAndChallenge *pkc = &spkc.pubKeyAndChallenge;
279 /* DER encoded spkc.pubKeyAndChallenge and spkc */
280 CSSM_DATA encodedPkc = {0, NULL};
281 CSSM_DATA encodedSpkc = {0, NULL};
282 CSSM_DATA signature = {0, NULL};
283 unsigned char *spkcB64 = NULL; // base64 encoded encodedSpkc
285 SecAccessRef accessRef;
288 CFStringRef result = NULL;
290 ortn = SecAccessCreate(keyDescription, NULL, &accessRef);
292 ERROR("***SecAccessCreate %d", ortn);
295 ortn = SecAccessCopySelectedACLList(accessRef, CSSM_ACL_AUTHORIZATION_DECRYPT, &acls);
297 ERROR("***SecAccessCopySelectedACLList %d", ortn);
300 acl = (SecACLRef)CFArrayGetValueAtIndex(acls, 0);
302 ortn = SecACLSetSimpleContents(acl, NULL, keyDescription, NULL);
304 ERROR("***SecACLSetSimpleContents %d", ortn);
308 // Cook up a key pair, just use any old params for now
309 ortn = SecKeyCreatePair(nil, // in default KC
310 GNR_KEY_ALG, // normally spec'd by user
311 keySize, // key size, ditto
313 CSSM_KEYUSE_ANY, // might want to restrict this
314 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE |
315 CSSM_KEYATTR_RETURN_REF, // pub attrs
316 CSSM_KEYUSE_ANY, // might want to restrict this
317 CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF |
318 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE,
323 ERROR("***SecKeyCreatePair %d", ortn);
327 /* get handle of CSPDL for crypto ops */
328 ortn = SecKeyGetCSPHandle(privKey, &cspHand);
330 ERROR("***SecKeyGetCSPHandle", ortn);
335 * Get the public key in encoded SubjectPublicKeyInfo form.
337 ortn = gnrGetSubjPubKey(cspHand, pubKey, &subjectPubKey);
341 freeSubjPubKey = true;
343 ortn = SecAsn1CoderCreate(&coder);
345 ERROR("***SecAsn1CoderCreate", ortn);
350 * Cook up PublicKeyAndChallenge and DER-encode it.
351 * First, DER-decode the key's SubjectPublicKeyInfo.
353 memset(&spkc, 0, sizeof(spkc));
355 ortn = SecAsn1DecodeData(coder, &subjectPubKey.KeyData, kSecAsn1SubjectPublicKeyInfoTemplate, &pkc->spki);
357 /* should never happen */
358 ERROR("***Error decoding subject public key info\n");
362 pkc->challenge.Length = CFStringGetLength(challenge);
363 if (pkc->challenge.Length == 0) {
364 pkc->challenge.Length = 1;
365 pkc->challenge.Data = (uint8 *)strdup("\0");
367 pkc->challenge.Data = (uint8 *)malloc(pkc->challenge.Length + 1);
368 CFStringGetCString(challenge, (char *)pkc->challenge.Data, pkc->challenge.Length + 1, kCFStringEncodingASCII);
370 ortn = SecAsn1EncodeItem(coder, pkc, PublicKeyAndChallengeTemplate, &encodedPkc);
372 /* should never happen */
373 ERROR("***Error encoding PublicKeyAndChallenge\n");
378 * Sign the encoded PublicKeyAndChallenge.
380 crtn = gnrSign(cspHand, &encodedPkc, privKey, GNR_SIG_ALG, &signature);
386 * Cook up SignedPublicKeyAndChallenge, DER-encode that.
387 * The PublicKeyAndChallenge stays in place where we originally
388 * created it before we signed it. Now we just add the algId
389 * and the signature proper.
391 spkc.algId.algorithm = GNR_SIG_ALGOID;
392 gnrNullAlgParams(&spkc.algId);
393 spkc.signature = signature;
394 /* convert to BIT length */
395 spkc.signature.Length *= 8;
396 ortn = SecAsn1EncodeItem(coder, &spkc, SignedPublicKeyAndChallengeTemplate, &encodedSpkc);
398 /* should never happen */
399 ERROR("***Error encoding SignedPublicKeyAndChallenge\n");
404 * Finally base64 the result and write that to outFile.
405 * cuEnc64() gives us a NULL-terminated string; we strip off the NULL.
407 spkcB64 = cuEnc64(encodedSpkc.Data, encodedSpkc.Length, &spkcB64Len);
408 if (spkcB64 == NULL) {
409 /* should never happen */
410 FATAL("***Error base64-encoding the result\n");
416 SecAsn1CoderRelease(coder);
418 if (freeSubjPubKey) {
419 CSSM_FreeKey(cspHand, NULL, &subjectPubKey, CSSM_FALSE);
421 if (signature.Data) {
422 gnrFreeCssmData(cspHand, &signature);
431 CFRelease(accessRef);
433 if (pkc->challenge.Data) {
434 free(pkc->challenge.Data);
437 result = CFStringCreateWithCString(NULL, (const char *)spkcB64, kCFStringEncodingASCII);
444 * Per-cert processing, called for each cert we extract from the
447 bool addCertificateToKeychainFromData(const unsigned char *certData,
448 unsigned certDataLen,
451 CSSM_DATA cert = {certDataLen, (uint8 *)certData};
452 SecCertificateRef certRef;
454 /* Make a SecCertificateRef */
455 OSStatus ortn = SecCertificateCreateFromData(&cert,
457 CSSM_CERT_ENCODING_DER,
460 ERROR("SecCertificateCreateFromData returned %d", (int)ortn);
465 * Add it to default keychain.
466 * Many people will be surprised that this op works without
467 * the user having to unlock the keychain.
469 ortn = SecCertificateAddToKeychain(certRef, nil);
471 /* Free the cert in any case */
476 case errSecDuplicateItem:
477 /* Not uncommon, definitely not an error */
478 ERROR("cert %u already present in keychain", certNum);
481 ERROR("SecCertificateAddToKeychain returned %d", (int)ortn);
488 WebCertificateParseResult addCertificatesToKeychainFromData(const void *bytes, unsigned length)
490 WebCertificateParseResult result = WebCertificateParseResultFailed;
492 /* DER-decode, first as NetscapeCertSequence */
493 SecAsn1CoderRef coder = NULL;
494 NetscapeCertSequence certSeq;
497 ortn = SecAsn1CoderCreate(&coder);
499 memset(&certSeq, 0, sizeof(certSeq));
500 ortn = SecAsn1Decode(coder, bytes, length, NetscapeCertSequenceTemplate, &certSeq);
502 if (certSeq.contentType.Length == CSSMOID_PKCS7_SignedData.Length &&
503 memcmp(certSeq.contentType.Data, CSSMOID_PKCS7_SignedData.Data, certSeq.contentType.Length) == 0) {
504 return WebCertificateParseResultPKCS7;
507 * Last cert is a root, which we do NOT want to add
508 * to the user's keychain.
510 unsigned numCerts = nssArraySize((const void **)certSeq.certs) - 1;
512 for (i=0; i<numCerts; i++) {
513 CSSM_DATA *cert = certSeq.certs[i];
514 result = addCertificateToKeychainFromData(cert->Data, cert->Length, i) ? WebCertificateParseResultSucceeded : WebCertificateParseResultFailed;
518 * Didn't appear to be a NetscapeCertSequence; assume it's just
519 * a cert. FIXME: Netscape spec says the blob might also be PKCS7
520 * format, which we're not handling here.
522 result = addCertificateToKeychainFromData(bytes, length, 0) ? WebCertificateParseResultSucceeded : WebCertificateParseResultFailed;
527 SecAsn1CoderRelease(coder);
533 #endif /* USE_NEW_KEY_GENERATION */