]> git.saurik.com Git - apple/security.git/blame - sec/Security/SecCertificateRequest.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / Security / SecCertificateRequest.c
CommitLineData
b1ab9ed8
A
1/*
2 * Copyright (c) 2008-2009 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 */
24
25#include <libDER/oids.h>
26#include <libDER/DER_Encode.h>
27
28#include <security_asn1/SecAsn1Types.h>
29#include <security_asn1/csrTemplates.h>
30#include <security_asn1/certExtensionTemplates.h>
31#include <security_asn1/secasn1.h>
32#include <security_asn1/SecAsn1Types.h>
33#include <security_asn1/oidsalg.h>
34#include <security_asn1/nameTemplates.h>
35
36#include <TargetConditionals.h>
37#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
38// ENABLE_CMS 0
39OSStatus SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2);
40#else
41// ENABLE_CMS 1
42#include <security_smime/cmspriv.h>
43#endif
44
45#include <Security/SecInternal.h>
46#include <Security/SecCertificatePriv.h>
47#include <Security/SecIdentity.h>
48#include <Security/SecCertificateInternal.h>
49#include <Security/SecItem.h>
50#include <Security/SecKey.h>
51#include <Security/SecRSAKey.h>
52#include <Security/SecKeyPriv.h>
53#include <CommonCrypto/CommonDigest.h>
427c49bc 54#include <CommonCrypto/CommonDigestSPI.h>
b1ab9ed8
A
55#include <CoreFoundation/CFNumber.h>
56#include <CoreFoundation/CFString.h>
57
58#include <AssertMacros.h>
59
60#include "SecCertificateRequest.h"
61
62CFTypeRef kSecOidCommonName = CFSTR("CN");
63CFTypeRef kSecOidCountryName = CFSTR("C");
64CFTypeRef kSecOidStateProvinceName = CFSTR("ST");
65CFTypeRef kSecOidLocalityName = CFSTR("L");
66CFTypeRef kSecOidOrganization = CFSTR("O");
67CFTypeRef kSecOidOrganizationalUnit = CFSTR("OU");
68//CFTypeRef kSecOidEmailAddress = CFSTR("1.2.840.113549.1.9.1");
69// keep natural order: C > ST > L > O > OU > CN > Email
70
71const unsigned char SecASN1PrintableString = SEC_ASN1_PRINTABLE_STRING;
72const unsigned char SecASN1UTF8String = SEC_ASN1_UTF8_STRING;
73
427c49bc 74static uint8_t * mod128_oid_encoding_ptr(uint8_t *ptr, uint32_t src, bool final)
b1ab9ed8
A
75{
76 if (src > 128)
427c49bc 77 ptr = mod128_oid_encoding_ptr(ptr, src / 128, false);
b1ab9ed8
A
78
79 unsigned char octet = src % 128;
80 if (!final)
81 octet |= 128;
427c49bc
A
82 *ptr++ = octet;
83
84 return ptr;
b1ab9ed8
A
85}
86
427c49bc 87static uint8_t * oid_der_data(PRArenaPool *poolp, CFStringRef oid_string, size_t *oid_data_len)
b1ab9ed8 88{
427c49bc
A
89 /* estimate encoded length from base 10 (4 bits) to base 128 (7 bits) */
90 size_t tmp_oid_length = ((CFStringGetLength(oid_string) * 4) / 7) + 1;
91 uint8_t *tmp_oid_data = PORT_ArenaAlloc(poolp, tmp_oid_length);
92 uint8_t *tmp_oid_data_ptr = tmp_oid_data;
93
b1ab9ed8 94 CFArrayRef oid = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
427c49bc 95 oid_string, CFSTR("."));
b1ab9ed8
A
96 CFIndex i = 0, count = CFArrayGetCount(oid);
97 SInt32 first_digit = 0, digit;
98 for (i = 0; i < count; i++) {
99 CFStringRef oid_octet = CFArrayGetValueAtIndex(oid, i);
100 SInt32 oid_octet_int_value = CFStringGetIntValue(oid_octet);
101 require(abs(oid_octet_int_value) != INT32_MAX, out);
102 if (i == 0)
103 first_digit = oid_octet_int_value;
104 else {
105 if (i == 1)
106 digit = 40 * first_digit + oid_octet_int_value;
107 else
108 digit = oid_octet_int_value;
427c49bc 109 tmp_oid_data_ptr = mod128_oid_encoding_ptr(tmp_oid_data_ptr, digit, true);
b1ab9ed8
A
110 }
111 }
112 CFReleaseSafe(oid);
427c49bc
A
113
114 *oid_data_len = tmp_oid_data_ptr - tmp_oid_data;
115 return tmp_oid_data;
b1ab9ed8 116out:
b1ab9ed8
A
117 return NULL;
118}
119
427c49bc 120
b1ab9ed8
A
121/*
122Get challenge password conversion and apply this:
123
124ASCII ? => PrintableString subset: [A-Za-z0-9 '()+,-./:=?] ?
125
126PrintableString > IA5String > UTF8String
127
128Consider using IA5String for email address
129*/
130
131static inline bool printable_string(CFStringRef string)
132{
133 bool result = true;
134
135 CFCharacterSetRef printable_charset =
136 CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault,
137 CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
138 "abcdefghijklmnopqrstuvwxyz"
139 "0123456789 '()+,-./:=?"));
140 CFCharacterSetRef not_printable_charset =
141 CFCharacterSetCreateInvertedSet(kCFAllocatorDefault, printable_charset);
142 CFRange found;
143 if (CFStringFindCharacterFromSet(string, not_printable_charset,
144 CFRangeMake(0, CFStringGetLength(string)), 0, &found))
145 result = false;
146
147 CFReleaseSafe(printable_charset);
148 CFReleaseSafe(not_printable_charset);
149
150 return result;
151}
152
153static bool make_nss_atv(PRArenaPool *poolp,
154 const void * oid, const void * value, const unsigned char type_in, NSS_ATV *nss_atv)
155{
156 size_t length = 0;
157 char *buffer = NULL;
158 unsigned char type = type_in;
159 if (CFGetTypeID(value) == CFStringGetTypeID()) {
160 length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value),
161 kCFStringEncodingUTF8);
162 buffer = PORT_ArenaAlloc(poolp, length);
163 /* TODO: Switch to using CFStringGetBytes,since this code will do the wrong thing for embedded 0's */
164 if (!CFStringGetCString(value, buffer, length, kCFStringEncodingASCII)) {
165 if (!CFStringGetCString(value, buffer, length, kCFStringEncodingUTF8))
166 return false;
167 if (type && type != SecASN1UTF8String)
168 return false;
169 type = SecASN1UTF8String;
170 }
171 else {
172 if (!type || type == SecASN1PrintableString) {
173 if (!printable_string(value))
174 type = SEC_ASN1_IA5_STRING;
175 else
176 type = SEC_ASN1_PRINTABLE_STRING;
177 }
178 }
179 length = strlen(buffer);
180 }
181 else if (CFGetTypeID(value) == CFDataGetTypeID()) {
182 /* will remain valid for the duration of the operation, still maybe copy into pool */
183 length = CFDataGetLength(value);
184 buffer = (char *)CFDataGetBytePtr(value);
185 }
186 size_t oid_length = 0;
187 uint8_t *oid_data = NULL;
188 if (CFGetTypeID(oid) == CFStringGetTypeID()) {
189 if (CFEqual(kSecOidCommonName, oid)) {
190 oid_length = oidCommonName.length; oid_data = oidCommonName.data;
191 } else if (CFEqual(kSecOidCountryName, oid)) {
192 oid_length = oidCountryName.length; oid_data = oidCountryName.data;
193 } else if (CFEqual(kSecOidStateProvinceName, oid)) {
194 oid_length = oidStateOrProvinceName.length; oid_data = oidStateOrProvinceName.data;
195 } else if (CFEqual(kSecOidLocalityName, oid)) {
196 oid_length = oidLocalityName.length; oid_data = oidLocalityName.data;
197 } else if (CFEqual(kSecOidOrganization, oid)) {
198 oid_length = oidOrganizationName.length; oid_data = oidOrganizationName.data;
199 } else if (CFEqual(kSecOidOrganizationalUnit, oid)) {
200 oid_length = oidOrganizationalUnitName.length; oid_data = oidOrganizationalUnitName.data;
201 } else {
427c49bc
A
202 oid_data = oid_der_data(poolp, oid, &oid_length);
203 require(oid_data, out);
b1ab9ed8
A
204 }
205 } else if (CFGetTypeID(oid) == CFDataGetTypeID()) {
206 /* will remain valid for the duration of the operation, still maybe copy into pool */
207 oid_length = CFDataGetLength(oid);
208 oid_data = (uint8_t *)CFDataGetBytePtr(oid);
209 }
210 NSS_ATV stage_nss_atv = { { oid_length, oid_data },
211 { { length, (uint8_t*)buffer }, type } };
212 *nss_atv = stage_nss_atv;
213 return true;
214out:
215 return false;
216}
217
218static NSS_RDN **make_subject(PRArenaPool *poolp, CFArrayRef subject)
219{
220 if (!subject)
221 return NULL;
222 CFIndex rdn_ix, rdn_count = CFArrayGetCount(subject);
223 NSS_RDN **rdnps = PORT_ArenaZNewArray(poolp, NSS_RDN *, rdn_count + 1);
224 NSS_RDN *rdns = PORT_ArenaZNewArray(poolp, NSS_RDN, rdn_count);
225 for (rdn_ix = 0; rdn_ix < rdn_count; rdn_ix++) {
226 rdnps[rdn_ix] = &rdns[rdn_ix];
227 CFArrayRef rdn = CFArrayGetValueAtIndex(subject, rdn_ix);
228 CFIndex atv_ix, atv_count = CFArrayGetCount(rdn);
229 rdns[rdn_ix].atvs = PORT_ArenaZNewArray(poolp, NSS_ATV *, atv_count + 1);
230 NSS_ATV *atvs = PORT_ArenaZNewArray(poolp, NSS_ATV, atv_count);
231 for (atv_ix = 0; atv_ix < atv_count; atv_ix++) {
232 rdns[rdn_ix].atvs[atv_ix] = &atvs[atv_ix];
233 CFArrayRef atv = CFArrayGetValueAtIndex(rdn, atv_ix);
234 if ((CFArrayGetCount(atv) != 2)
235 || !make_nss_atv(poolp, CFArrayGetValueAtIndex(atv, 0),
236 CFArrayGetValueAtIndex(atv, 1), 0, &atvs[atv_ix]))
237 return NULL;
238 }
239 }
240 return rdnps;
241}
242
243struct make_general_names_context {
244 PRArenaPool *poolp;
245 SecAsn1Item *names;
246 uint32_t count;
247 uint32_t capacity;
248};
249
250static void make_general_names(const void *key, const void *value, void *context)
251{
252 struct make_general_names_context *gn = (struct make_general_names_context *)context;
253 require(value,out);
254 CFArrayRef gn_values = NULL;
255 CFStringRef gn_value = NULL;
256 CFIndex entry_ix, entry_count = 0;
257 if (CFGetTypeID(value) == CFArrayGetTypeID()) {
258 gn_values = (CFArrayRef)value;
259 entry_count = CFArrayGetCount(value);
260 } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
261 gn_value = (CFStringRef)value;
262 entry_count = 1;
263 }
264
265 require(entry_count > 0, out);
266
267 require(key,out);
268 require(CFGetTypeID(key) == CFStringGetTypeID(), out);
269
270 if (!gn->names || (gn->count == gn->capacity)) {
271 uint32_t capacity = gn->capacity;
272 if (capacity)
273 capacity *= 2;
274 else
275 capacity = 10;
276
277 void * new_array = PORT_ArenaZNewArray(gn->poolp, SecAsn1Item, capacity);
278 if (gn->names)
279 memcpy(new_array, gn->names, gn->capacity);
280 gn->names = new_array;
281 gn->capacity = capacity;
282 }
283
284 NSS_GeneralName general_name_item = { { }, -1 };
285 if (kCFCompareEqualTo == CFStringCompare(CFSTR("dNSName"), key, kCFCompareCaseInsensitive))
286 general_name_item.tag = NGT_DNSName;
287 else if (kCFCompareEqualTo == CFStringCompare(CFSTR("rfc822Name"), key, kCFCompareCaseInsensitive))
288 general_name_item.tag = NGT_RFC822Name;
289 else if (kCFCompareEqualTo == CFStringCompare(CFSTR("uniformResourceIdentifier"), key, kCFCompareCaseInsensitive))
290 general_name_item.tag = NGT_URI;
291 else if (kCFCompareEqualTo == CFStringCompare(CFSTR("ntPrincipalName"), key, kCFCompareCaseInsensitive))
292 {
293 /*
294 NT Principal in SubjectAltName is defined in the context of Smartcards:
295
296 http://www.oid-info.com/get/1.3.6.1.4.1.311.20.2.3
297 http://support.microsoft.com/default.aspx?scid=kb;en-us;281245
298
299 Subject Alternative Name = Other Name: Principal Name= (UPN). For example:
300 UPN = user1@name.com
301 The UPN OtherName OID is : "1.3.6.1.4.1.311.20.2.3"
302 The UPN OtherName value: Must be ASN1-encoded UTF8 string
303 Subject = Distinguished name of user. This field is a mandatory extension, but the population of this field is optional.
304 */
305
306 /* OtherName ::= SEQUENCE {
307 type-id OBJECT IDENTIFIER,
308 value [0] EXPLICIT ANY DEFINED BY type-id
309 } */
310 uint8_t nt_principal_oid[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03 };
311 typedef struct {
312 SecAsn1Oid typeId;
313 SecAsn1Item value;
314 } nt_principal_other_name;
315 nt_principal_other_name name = {};
316
317 const SecAsn1Template my_other_name_template[] = {
318 { SEC_ASN1_SEQUENCE,
319 0, NULL, sizeof(nt_principal_other_name) },
320 { SEC_ASN1_OBJECT_ID,
321 offsetof(nt_principal_other_name,typeId), },
322 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | 0, offsetof(nt_principal_other_name,value), kSecAsn1UTF8StringTemplate, },
323 { 0, }
324 };
325 const SecAsn1Template my_other_name_template_cons[] = {
326 { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | NGT_OtherName,
327 0, my_other_name_template, sizeof(nt_principal_other_name) }
328 };
329
330 size_t length = 0;
331 char *buffer = NULL;
332
333 require(gn_value, out);
334 require(CFGetTypeID(gn_value) == CFStringGetTypeID(), out);
335 length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value),
336 kCFStringEncodingUTF8);
337 buffer = PORT_ArenaAlloc(gn->poolp, length);
338 if (!CFStringGetCString(value, buffer, length, kCFStringEncodingUTF8))
339 goto out;
340
341 name.typeId.Length = sizeof(nt_principal_oid);
342 name.typeId.Data = nt_principal_oid;
343 name.value.Length = strlen(buffer);
344 name.value.Data = (uint8_t*)buffer;
345 SEC_ASN1EncodeItem(gn->poolp, &gn->names[gn->count], &name, my_other_name_template_cons);
346 gn->count++;
347
348 /* We already encoded the value for the general name */
349 goto out;
350 }
351 else
352 goto out;
353
354 if (gn_values) {
355 for (entry_ix = 0; entry_ix < entry_count; entry_ix++) {
356 CFTypeRef entry_value = CFArrayGetValueAtIndex(gn_values, entry_ix);
357 CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef)entry_value),
358 kCFStringEncodingUTF8); /* we only allow ASCII => only expect IA5Strings */
359 char *buffer = (char *)PORT_ArenaZNewArray(gn->poolp, uint8_t, buffer_size);
360 require(CFStringGetCString((CFStringRef)entry_value, buffer, buffer_size, kCFStringEncodingASCII), out);
361 general_name_item.item.Data = (uint8_t*)buffer;
362 general_name_item.item.Length = strlen(buffer);
363 SEC_ASN1EncodeItem(gn->poolp, &gn->names[gn->count], &general_name_item, kSecAsn1GeneralNameTemplate);
364 gn->count++;
365 }
366 } else if (gn_value) {
367 CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(gn_value),
368 kCFStringEncodingUTF8);
369 char *buffer = (char *)PORT_ArenaZNewArray(gn->poolp, uint8_t, buffer_size);
370 require(CFStringGetCString(gn_value, buffer, buffer_size, kCFStringEncodingASCII), out);
371 general_name_item.item.Data = (uint8_t*)buffer;
372 general_name_item.item.Length = strlen(buffer);
373 SEC_ASN1EncodeItem(gn->poolp, &gn->names[gn->count], &general_name_item, kSecAsn1GeneralNameTemplate);
374 gn->count++;
375 }
376out:
377 return;
378}
379
380static SecAsn1Item make_subjectAltName_extension(PRArenaPool *poolp, CFDictionaryRef subjectAltNames)
381{
382 SecAsn1Item subjectAltExt = {};
383
384 struct make_general_names_context context = { poolp, NULL, 0 };
385 CFDictionaryApplyFunction(subjectAltNames, make_general_names, &context);
386
387 // all general names in a sequence:
388 uint32_t ix;
389 SecAsn1Item **general_names = PORT_ArenaZNewArray(poolp, SecAsn1Item *, context.count + 1);
390 for (ix = 0; ix < context.count; ix++)
391 general_names[ix] = &context.names[ix];
392 NSS_GeneralNames gnames = { general_names };
393 SEC_ASN1EncodeItem(poolp, &subjectAltExt, &gnames, kSecAsn1GeneralNamesTemplate);
394
395 return subjectAltExt;
396}
397
398CFTypeRef kSecCSRChallengePassword = CFSTR("csrChallengePassword");
399CFTypeRef kSecSubjectAltName = CFSTR("subjectAltName");
400CFTypeRef kSecCertificateKeyUsage = CFSTR("keyUsage");
401CFTypeRef kSecCSRBasicContraintsPathLen = CFSTR("basicConstraints");
427c49bc 402CFTypeRef kSecCertificateExtensions = CFSTR("certificateExtensions");
b1ab9ed8
A
403
404static const uint8_t pkcs9ExtensionsRequested[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 14 };
405static const uint8_t pkcs9ChallengePassword[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 7 };
406
407static const uint8_t encoded_asn1_true = 0xFF;
408static const SecAsn1Item asn1_true =
409 { sizeof(encoded_asn1_true), (uint8_t*)&encoded_asn1_true };
410
411static inline uint32_t highest_bit(uint32_t n)
412{
413 return ((n) >> 16 ? ((n)>>=16, 16) : 0) + \
414 ((n) >> 8 ? ((n)>>=8, 8) : 0) + \
415 ((n) >> 4 ? ((n)>>=4, 4) : 0) + \
416 ((n) >> 2 ? ((n)>>=2, 2) : 0) + \
417 ((n) >> 1 ? ((n)>>=1, 1) : 0) + \
418 (n);
419}
420
427c49bc
A
421struct add_custom_extension_args {
422 PLArenaPool *poolp;
423 NSS_CertExtension *csr_extension;
424 uint32_t num_extensions;
425 uint32_t max_extensions;
426};
427
428static void add_custom_extension(const void *key, const void *value, void *context)
429{
430 struct add_custom_extension_args *args = (struct add_custom_extension_args *)context;
431 size_t der_data_len;
432
433 require(args->num_extensions < args->max_extensions, out);
434
435 uint8_t * der_data = oid_der_data(args->poolp, key, &der_data_len);
436 SecAsn1Item encoded_value = {};
437
438 if (CFGetTypeID(value) == CFStringGetTypeID()) {
439 CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), kCFStringEncodingUTF8);
440 char *buffer = (char *)PORT_ArenaZNewArray(args->poolp, uint8_t, buffer_size);
441 if (!CFStringGetCString(value, buffer, buffer_size, kCFStringEncodingUTF8))
442 goto out;
443
444 SecAsn1Item buffer_item = { strlen(buffer), (uint8_t*)buffer };
445 SEC_ASN1EncodeItem(args->poolp, &encoded_value, &buffer_item, kSecAsn1UTF8StringTemplate);
446 } else if (CFGetTypeID(value) == CFDataGetTypeID()) {
447 SecAsn1Item data_item = { CFDataGetLength(value), (uint8_t*)CFDataGetBytePtr(value) };
448 SEC_ASN1EncodeItem(args->poolp, &encoded_value, &data_item, kSecAsn1OctetStringTemplate);
449 } else
450 goto out;
451
452
453 if (der_data && encoded_value.Length) {
454 args->csr_extension[args->num_extensions].value = encoded_value;
455 args->csr_extension[args->num_extensions].extnId.Length = der_data_len;
456 args->csr_extension[args->num_extensions].extnId.Data = der_data;
457 args->num_extensions++;
458 }
459out:
460 return;
461}
462
b1ab9ed8
A
463static
464NSS_CertExtension **
465extensions_from_parameters(PRArenaPool *poolp, CFDictionaryRef parameters)
466{
427c49bc
A
467 uint32_t num_extensions = 0, max_extensions = 10;
468 NSS_CertExtension **csr_extensions = PORT_ArenaZNewArray(poolp, NSS_CertExtension *, max_extensions + 1); /* NULL terminated array */
b1ab9ed8
A
469 NSS_CertExtension *csr_extension = PORT_ArenaZNewArray(poolp, NSS_CertExtension, max_extensions);
470
471 CFNumberRef basic_contraints_num = CFDictionaryGetValue(parameters, kSecCSRBasicContraintsPathLen);
472 if (basic_contraints_num) {
473 NSS_BasicConstraints basic_contraints = { asn1_true, {} };
474 uint8_t path_len;
475
476 int basic_contraints_path_len = 0;
477 require(CFNumberGetValue(basic_contraints_num, kCFNumberIntType, &basic_contraints_path_len), out);
478 if (basic_contraints_path_len >= 0 && basic_contraints_path_len < 256) {
427c49bc 479 path_len = (uint8_t)basic_contraints_path_len;
b1ab9ed8
A
480 basic_contraints.pathLenConstraint.Length = sizeof(path_len);
481 basic_contraints.pathLenConstraint.Data = &path_len;
482 }
483
484 csr_extension[num_extensions].extnId.Data = oidBasicConstraints.data;
485 csr_extension[num_extensions].extnId.Length = oidBasicConstraints.length;
486 csr_extension[num_extensions].critical = asn1_true;
487
488 SEC_ASN1EncodeItem(poolp, &csr_extension[num_extensions].value, &basic_contraints,
489 kSecAsn1BasicConstraintsTemplate);
490 require(num_extensions++ < max_extensions, out);
491 }
492
493 CFDictionaryRef subject_alternate_names = CFDictionaryGetValue(parameters, kSecSubjectAltName);
494 if (subject_alternate_names) {
495 require(CFGetTypeID(subject_alternate_names) == CFDictionaryGetTypeID(), out);
496 csr_extension[num_extensions].value = make_subjectAltName_extension(poolp, subject_alternate_names);
497 /* set up subjectAltName cert request value */
498 csr_extension[num_extensions].extnId.Length = oidSubjectAltName.length;
499 csr_extension[num_extensions].extnId.Data = oidSubjectAltName.data;
500 require(num_extensions++ < max_extensions, out);
501 }
502
503 CFNumberRef key_usage_requested = CFDictionaryGetValue(parameters, kSecCertificateKeyUsage);
504 SecAsn1Item key_usage_asn1_value = { 0 };
505 if (key_usage_requested) {
506 int key_usage_value;
507 require(CFNumberGetValue(key_usage_requested, kCFNumberIntType, &key_usage_value), out);
508 if (key_usage_value > 0) {
509 uint32_t key_usage_value_be = 0, key_usage_mask = 1<<31;
510 uint32_t key_usage_value_max_bitlen = 9, key_usage_value_bitlen = 0;
511 while(key_usage_value_max_bitlen) {
512 if (key_usage_value & 1) {
513 key_usage_value_be |= key_usage_mask;
514 key_usage_value_bitlen = 10 - key_usage_value_max_bitlen;
515 }
516 key_usage_value >>= 1;
517 key_usage_value_max_bitlen--;
518 key_usage_mask >>= 1;
519 }
520
521 SecAsn1Item key_usage_input = { key_usage_value_bitlen,
522 ((uint8_t*)&key_usage_value_be) + 3 - (key_usage_value_bitlen >> 3) };
523 SEC_ASN1EncodeItem(poolp, &key_usage_asn1_value, &key_usage_input, kSecAsn1BitStringTemplate);
524
525 csr_extension[num_extensions].extnId.Data = oidKeyUsage.data;
526 csr_extension[num_extensions].extnId.Length = oidKeyUsage.length;
527 csr_extension[num_extensions].critical = asn1_true;
528 csr_extension[num_extensions].value = key_usage_asn1_value;
529 require(num_extensions++ < max_extensions, out);
530 }
531 }
532
427c49bc
A
533 CFDictionaryRef custom_extension_requested = CFDictionaryGetValue(parameters, kSecCertificateExtensions);
534 if (custom_extension_requested) {
535 require(CFGetTypeID(custom_extension_requested) == CFDictionaryGetTypeID(), out);
536 struct add_custom_extension_args args = {
537 poolp,
538 csr_extension,
539 num_extensions,
540 max_extensions
541 };
542 CFDictionaryApplyFunction(custom_extension_requested, add_custom_extension, &args);
543 num_extensions = args.num_extensions;
544 }
545
b1ab9ed8
A
546 /* extensions requested (subjectAltName, keyUsage) sequence of extension sequences */
547 uint32_t ix = 0;
427c49bc 548 for (ix = 0; ix < num_extensions; ix++)
b1ab9ed8
A
549 csr_extensions[ix] = csr_extension[ix].extnId.Length ? &csr_extension[ix] : NULL;
550
551out:
552 return csr_extensions;
553}
554
427c49bc
A
555
556
b1ab9ed8
A
557static
558NSS_Attribute **nss_attributes_from_parameters_dict(PRArenaPool *poolp, CFDictionaryRef parameters)
559{
560 /* A challenge-password attribute must have a single attribute value.
561
562 ChallengePassword attribute values generated in accordance with this
563 version of this document SHOULD use the PrintableString encoding
564 whenever possible. If internationalization issues make this
565 impossible, the UTF8String alternative SHOULD be used. PKCS #9-
566 attribute processing systems MUST be able to recognize and process
567 all string types in DirectoryString values.
568
569 Upperbound of 255 defined for all PKCS#9 attributes.
570
571 pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840)
572 rsadsi(113549) pkcs(1) 9}
573 pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7}
574
575 */
576 if (!parameters)
577 return NULL;
578 uint32_t num_attrs = 0;
579
580 CFStringRef challenge = CFDictionaryGetValue(parameters, kSecCSRChallengePassword);
581 NSS_Attribute challenge_password_attr = {};
582 if (challenge) {
583 CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(challenge),
584 kCFStringEncodingUTF8); /* we only allow UTF8 or ASCII */
585 char *buffer = (char *)PORT_ArenaZNewArray(poolp, uint8_t, buffer_size);
586 bool utf8 = false;
587 if (!CFStringGetCString(challenge, buffer, buffer_size, kCFStringEncodingASCII)) {
588 if (!CFStringGetCString(challenge, buffer, buffer_size, kCFStringEncodingUTF8))
589 return NULL;
590 utf8 = true;
591 } else
592 if (!printable_string(challenge))
593 utf8 = true;
594
595 SecAsn1Item *challenge_password_value = PORT_ArenaZNewArray(poolp, SecAsn1Item, 1);
596 SecAsn1Item challenge_password_raw = { strlen(buffer), (uint8_t*)buffer };
597 SEC_ASN1EncodeItem(poolp, challenge_password_value, &challenge_password_raw,
598 utf8 ? kSecAsn1UTF8StringTemplate : kSecAsn1PrintableStringTemplate);
599 SecAsn1Item **challenge_password_values = PORT_ArenaZNewArray(poolp, SecAsn1Item *, 2);
600 challenge_password_values[0] = challenge_password_value;
601 challenge_password_attr.attrType.Length = sizeof(pkcs9ChallengePassword);
602 challenge_password_attr.attrType.Data = (uint8_t*)&pkcs9ChallengePassword;
603 challenge_password_attr.attrValue = challenge_password_values;
604 num_attrs++;
605 }
606
607 NSS_CertExtension **extensions = extensions_from_parameters(poolp, parameters);
608 NSS_Attribute extensions_requested_attr = {};
609 if (extensions) {
610 SecAsn1Item *extensions_requested_value = PORT_ArenaZNewArray(poolp, SecAsn1Item, 1);
611 SEC_ASN1EncodeItem(poolp, extensions_requested_value, &extensions, kSecAsn1SequenceOfCertExtensionTemplate);
612 SecAsn1Item **extensions_requested_values = PORT_ArenaZNewArray(poolp, SecAsn1Item *, 2);
613 extensions_requested_values[0] = extensions_requested_value;
614 extensions_requested_values[1] = NULL;
615 extensions_requested_attr.attrType.Length = sizeof(pkcs9ExtensionsRequested);
616 extensions_requested_attr.attrType.Data = (uint8_t*)pkcs9ExtensionsRequested;
617 extensions_requested_attr.attrValue = extensions_requested_values;
618 num_attrs++;
619 }
620
621 NSS_Attribute **attributes_ptr = PORT_ArenaZNewArray(poolp, NSS_Attribute *, num_attrs + 1);
622 NSS_Attribute *attributes = PORT_ArenaZNewArray(poolp, NSS_Attribute, num_attrs);
623 if (challenge_password_attr.attrType.Length) {
624 --num_attrs;
625 attributes[num_attrs] = challenge_password_attr;
626 attributes_ptr[num_attrs] = &attributes[num_attrs];
627 }
628 if (extensions_requested_attr.attrType.Length) {
629 --num_attrs;
630 attributes[num_attrs] = extensions_requested_attr;
631 attributes_ptr[num_attrs] = &attributes[num_attrs];
632 }
633 return attributes_ptr;
634#if 0
635out:
636 return NULL;
637#endif
638}
639
640static const uint8_t encoded_null[2] = { SEC_ASN1_NULL, 0 };
641static const SecAsn1Item asn1_null = { sizeof(encoded_null), (uint8_t*)encoded_null };
642
643CFDataRef SecGenerateCertificateRequestWithParameters(SecRDN *subject,
644 CFDictionaryRef parameters, SecKeyRef publicKey, SecKeyRef privateKey)
645{
646 CFDataRef csr = NULL;
647 PRArenaPool *poolp = PORT_NewArena(1024);
648 CFDictionaryRef pubkey_attrs = NULL;
649
650 if (!poolp)
651 return NULL;
652
653 NSSCertRequest certReq;
654 memset(&certReq, 0, sizeof(certReq));
655
656 /* version */
657 unsigned char version = 0;
658 certReq.reqInfo.version.Length = sizeof(version);
659 certReq.reqInfo.version.Data = &version;
660
661 /* subject */
662 unsigned atv_num = 0, num = 0;
663 SecRDN *one_rdn;
664 SecATV *one_atv;
665 for (one_rdn = subject; *one_rdn; one_rdn++) {
666 for (one_atv = *one_rdn; one_atv->oid; one_atv++)
667 atv_num++;
668 atv_num++; /* one more */
669 num++;
670 }
671 NSS_ATV atvs[atv_num];
672 NSS_ATV *atvps[atv_num];
673 NSS_RDN rdns[num];
674 NSS_RDN *rdnps[num+1];
675 atv_num = 0;
676 unsigned rdn_num = 0;
677 for (one_rdn = subject; *one_rdn; one_rdn++) {
678 rdns[rdn_num].atvs = &atvps[atv_num];
679 rdnps[rdn_num] = &rdns[rdn_num];
680 rdn_num++;
681 for (one_atv = *one_rdn; one_atv->oid; one_atv++) {
682 if (!make_nss_atv(poolp, one_atv->oid, one_atv->value,
683 one_atv->type, &atvs[atv_num]))
684 return NULL;
685 atvps[atv_num] = &atvs[atv_num];
686 atv_num++;
687 }
688 atvps[atv_num++] = NULL;
689 }
690 rdnps[rdn_num] = NULL;
691 certReq.reqInfo.subject.rdns = rdnps;
692
693 /* public key info */
694 certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Length = oidRsa.length;
695 certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Data = oidRsa.data;
696 certReq.reqInfo.subjectPublicKeyInfo.algorithm.parameters = asn1_null;
697
698 pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey);
699 CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData);
700 uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)];
701 size_t signature_length = sizeof(signature);
702
703 certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey);
704 certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey);
705
706 certReq.reqInfo.attributes = nss_attributes_from_parameters_dict(poolp, parameters);
707 SecCmsArraySortByDER((void **)certReq.reqInfo.attributes, kSecAsn1AttributeTemplate, NULL);
708
709 /* encode request info by itself to calculate signature */
710 SecAsn1Item reqinfo = {};
711 SEC_ASN1EncodeItem(poolp, &reqinfo, &certReq.reqInfo, kSecAsn1CertRequestInfoTemplate);
b1ab9ed8
A
712
713 /* calculate signature */
714 uint8_t reqinfo_hash[CC_SHA1_DIGEST_LENGTH];
427c49bc 715 CCDigest(kCCDigestSHA1, reqinfo.Data, (CC_LONG)reqinfo.Length, reqinfo_hash);
b1ab9ed8
A
716 require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1,
717 reqinfo_hash, sizeof(reqinfo_hash), signature, &signature_length), out);
718
719 /* signature and info */
720 certReq.signatureAlgorithm.algorithm.Length = oidSha1Rsa.length;
721 certReq.signatureAlgorithm.algorithm.Data = oidSha1Rsa.data;
722 certReq.signatureAlgorithm.parameters = asn1_null;
723 certReq.signature.Data = signature;
724 certReq.signature.Length = signature_length * 8;
725
726 /* encode csr */
727 SecAsn1Item cert_request = {};
728 require_quiet(SEC_ASN1EncodeItem(poolp, &cert_request, &certReq,
729 kSecAsn1CertRequestTemplate), out);
730 csr = CFDataCreate(kCFAllocatorDefault, cert_request.Data, cert_request.Length);
731
732out:
733 if (poolp)
734 PORT_FreeArena(poolp, PR_TRUE);
735 CFReleaseSafe(pubkey_attrs);
736 return csr;
737}
738
739CFDataRef SecGenerateCertificateRequest(CFArrayRef subject,
740 CFDictionaryRef parameters, SecKeyRef publicKey, SecKeyRef privateKey)
741{
742 CFDataRef csr = NULL;
743 PRArenaPool *poolp = PORT_NewArena(1024);
744 CFDictionaryRef pubkey_attrs = NULL;
745
746 if (!poolp)
747 return NULL;
748
749 NSSCertRequest certReq;
750 memset(&certReq, 0, sizeof(certReq));
751
752 /* version */
753 unsigned char version = 0;
754 certReq.reqInfo.version.Length = sizeof(version);
755 certReq.reqInfo.version.Data = &version;
756
757 /* subject */
758 certReq.reqInfo.subject.rdns = make_subject(poolp, (CFArrayRef)subject);
759
760 /* public key info */
761 certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Length = oidRsa.length;
762 certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Data = oidRsa.data;
763 certReq.reqInfo.subjectPublicKeyInfo.algorithm.parameters = asn1_null;
764
765 pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey);
766 CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData);
767 uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)];
768 size_t signature_length = sizeof(signature);
769
770 certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey);
771 certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey);
772
773 certReq.reqInfo.attributes = nss_attributes_from_parameters_dict(poolp, parameters);
774 SecCmsArraySortByDER((void **)certReq.reqInfo.attributes, kSecAsn1AttributeTemplate, NULL);
775
776 /* encode request info by itself to calculate signature */
777 SecAsn1Item reqinfo = {};
778 SEC_ASN1EncodeItem(poolp, &reqinfo, &certReq.reqInfo, kSecAsn1CertRequestInfoTemplate);
b1ab9ed8
A
779
780 /* calculate signature */
781 uint8_t reqinfo_hash[CC_SHA1_DIGEST_LENGTH];
427c49bc 782 CCDigest(kCCDigestSHA1, reqinfo.Data, reqinfo.Length, reqinfo_hash);
b1ab9ed8
A
783 require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1,
784 reqinfo_hash, sizeof(reqinfo_hash), signature, &signature_length), out);
785
786 /* signature and info */
787 certReq.signatureAlgorithm.algorithm.Length = oidSha1Rsa.length;
788 certReq.signatureAlgorithm.algorithm.Data = oidSha1Rsa.data;
789 certReq.signatureAlgorithm.parameters = asn1_null;
790 certReq.signature.Data = signature;
791 certReq.signature.Length = signature_length * 8;
792
793 /* encode csr */
794 SecAsn1Item cert_request = {};
795 require_quiet(SEC_ASN1EncodeItem(poolp, &cert_request, &certReq,
796 kSecAsn1CertRequestTemplate), out);
797 csr = CFDataCreate(kCFAllocatorDefault, cert_request.Data, cert_request.Length);
798
799out:
800 if (poolp)
801 PORT_FreeArena(poolp, PR_TRUE);
802 CFReleaseSafe(pubkey_attrs);
803 return csr;
804}
805
806bool SecVerifyCertificateRequest(CFDataRef csr, SecKeyRef *publicKey,
807 CFStringRef *challenge, CFDataRef *subject, CFDataRef *extensions)
808{
809 PRArenaPool *poolp = PORT_NewArena(1024);
810 SecKeyRef candidatePublicKey = NULL;
811 bool valid = false;
812 NSSCertRequest certReq;
813 memset(&certReq, 0, sizeof(certReq));
814 SecAsn1Item csr_item = { CFDataGetLength(csr), (uint8_t*)CFDataGetBytePtr(csr) };
815 require_noerr_quiet(SEC_ASN1DecodeItem(poolp, &certReq, kSecAsn1CertRequestTemplate,
816 &csr_item), out);
817
818 /* signature and info */
819 require(certReq.signatureAlgorithm.algorithm.Length == oidSha1Rsa.length, out);
820 require_noerr(memcmp(oidSha1Rsa.data, certReq.signatureAlgorithm.algorithm.Data,
821 oidSha1Rsa.length), out);
822 require(certReq.signatureAlgorithm.parameters.Length == asn1_null.Length, out);
823 require_noerr(memcmp(asn1_null.Data, certReq.signatureAlgorithm.parameters.Data,
824 asn1_null.Length), out);
825
826 /* encode request info by itself to calculate signature */
827 SecAsn1Item reqinfo = {};
828 SEC_ASN1EncodeItem(poolp, &reqinfo, &certReq.reqInfo, kSecAsn1CertRequestInfoTemplate);
829
830 /* calculate signature */
831 uint8_t reqinfo_hash[CC_SHA1_DIGEST_LENGTH];
832 require(reqinfo.Length<=UINT32_MAX, out);
427c49bc 833 CCDigest(kCCDigestSHA1, reqinfo.Data, (CC_LONG)reqinfo.Length, reqinfo_hash);
b1ab9ed8
A
834
835 /* @@@ check for version 0 */
836
837 require(candidatePublicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault,
838 certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Data,
839 certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Length / 8,
840 kSecKeyEncodingPkcs1), out);
841
842 require_noerr_quiet(SecKeyRawVerify(candidatePublicKey, kSecPaddingPKCS1SHA1,
843 reqinfo_hash, sizeof(reqinfo_hash),
844 certReq.signature.Data, certReq.signature.Length / 8), out);
845
846 SecAsn1Item subject_item = { 0 }, extensions_item = { 0 }, challenge_item = { 0 };
847 require_quiet(SEC_ASN1EncodeItem(poolp, &subject_item,
848 &certReq.reqInfo.subject, kSecAsn1NameTemplate), out);
849
850 if (*certReq.reqInfo.attributes) {
851 uint32_t ix;
852 for (ix = 0; certReq.reqInfo.attributes[ix]; ix++) {
853 NSS_Attribute *attr = certReq.reqInfo.attributes[ix];
854 if ( (sizeof(pkcs9ChallengePassword) == attr->attrType.Length) &&
855 !memcmp(pkcs9ChallengePassword, attr->attrType.Data, sizeof(pkcs9ChallengePassword)))
856 challenge_item = *attr->attrValue[0];
857 else if ( (sizeof(pkcs9ExtensionsRequested) == attr->attrType.Length) &&
858 !memcmp(pkcs9ExtensionsRequested, attr->attrType.Data, sizeof(pkcs9ExtensionsRequested)))
859 extensions_item = *attr->attrValue[0];
860 }
861 }
862
863 if (subject && subject_item.Length)
864 *subject = CFDataCreate(kCFAllocatorDefault, subject_item.Data, subject_item.Length);
865 if (extensions && extensions_item.Length)
866 *extensions = CFDataCreate(kCFAllocatorDefault, extensions_item.Data, extensions_item.Length);
867 if (challenge && challenge_item.Length) {
868 SecAsn1Item string = { 0 };
869 SECStatus rv = SEC_ASN1DecodeItem(poolp, &string, kSecAsn1UTF8StringTemplate, &challenge_item);
870 if (rv)
871 rv = SEC_ASN1DecodeItem(poolp, &string, kSecAsn1PrintableStringTemplate, &challenge_item);
872 if (!rv)
873 *challenge = CFStringCreateWithBytes(kCFAllocatorDefault, string.Data, string.Length, kCFStringEncodingUTF8, false);
874 else
875 *challenge = NULL;
876 }
877 if (publicKey) {
878 *publicKey = candidatePublicKey;
879 candidatePublicKey = NULL;
880 }
881 valid = true;
882out:
883 CFReleaseSafe(candidatePublicKey);
884 if (poolp)
885 PORT_FreeArena(poolp, PR_TRUE);
886 return valid;
887}
888
889#define HIDIGIT(v) (((v) / 10) + '0')
890#define LODIGIT(v) (((v) % 10) + '0')
891
892static OSStatus
893DER_CFDateToUTCTime(PRArenaPool *poolp, CFAbsoluteTime date, SecAsn1Item * utcTime)
894{
895 CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(date, NULL /* GMT */);
896 unsigned char *d;
897 SInt8 second;
898
899 utcTime->Length = 13;
900 utcTime->Data = d = PORT_ArenaAlloc(poolp, 13);
901 if (!utcTime->Data)
902 return SECFailure;
903
904 /* UTC time does not handle the years before 1950 */
905 if (gdate.year < 1950)
906 return SECFailure;
907
908 /* remove the century since it's added to the year by the
909 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
910 gdate.year %= 100;
911 second = gdate.second;
912
913 d[0] = HIDIGIT(gdate.year);
914 d[1] = LODIGIT(gdate.year);
915 d[2] = HIDIGIT(gdate.month);
916 d[3] = LODIGIT(gdate.month);
917 d[4] = HIDIGIT(gdate.day);
918 d[5] = LODIGIT(gdate.day);
919 d[6] = HIDIGIT(gdate.hour);
920 d[7] = LODIGIT(gdate.hour);
921 d[8] = HIDIGIT(gdate.minute);
922 d[9] = LODIGIT(gdate.minute);
923 d[10] = HIDIGIT(second);
924 d[11] = LODIGIT(second);
925 d[12] = 'Z';
926 return SECSuccess;
927}
928
929SecCertificateRef
930SecGenerateSelfSignedCertificate(CFArrayRef subject, CFDictionaryRef parameters,
931 SecKeyRef publicKey, SecKeyRef privateKey)
932{
933 SecCertificateRef cert = NULL;
934 PRArenaPool *poolp = PORT_NewArena(1024);
935 CFDictionaryRef pubkey_attrs = NULL;
936 if (!poolp)
937 return NULL;
938
939 NSS_Certificate cert_tmpl;
940 memset(&cert_tmpl, 0, sizeof(cert_tmpl));
941
942 /* version */
943 unsigned char version = 2;
944 cert_tmpl.tbs.version.Length = sizeof(version);
945 cert_tmpl.tbs.version.Data = &version;
946
947 /* serialno */
948 unsigned char serialNumber = 1;
949 cert_tmpl.tbs.serialNumber.Length = sizeof(serialNumber);
950 cert_tmpl.tbs.serialNumber.Data = &serialNumber;
951
952 /* subject/issuer */
953 cert_tmpl.tbs.issuer.rdns = make_subject(poolp, (CFArrayRef)subject);
954 cert_tmpl.tbs.subject.rdns = cert_tmpl.tbs.issuer.rdns;
955
956 DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent(), &cert_tmpl.tbs.validity.notBefore.item);
957 cert_tmpl.tbs.validity.notBefore.tag = SEC_ASN1_UTC_TIME;
958 DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl.tbs.validity.notAfter.item);
959 cert_tmpl.tbs.validity.notAfter.tag = SEC_ASN1_UTC_TIME;
960
961 /* extensions */
962 cert_tmpl.tbs.extensions = extensions_from_parameters(poolp, parameters);
963
964 /* @@@ we only handle rsa keys */
965 pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey);
966 CFTypeRef key_type = CFDictionaryGetValue(pubkey_attrs, kSecAttrKeyType);
967 if (key_type && CFEqual(key_type, kSecAttrKeyTypeRSA)) {
968 /* public key data and algorithm */
969 cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.algorithm = CSSMOID_RSA;
970 cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.parameters = asn1_null;
971
972 CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData);
973 cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey);
974 cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey);
975
976 /* signature algorithm */
977 cert_tmpl.tbs.signature.algorithm = CSSMOID_SHA1WithRSA;
978 cert_tmpl.tbs.signature.parameters = asn1_null;
979 cert_tmpl.signatureAlgorithm.algorithm = CSSMOID_SHA1WithRSA;
980 cert_tmpl.signatureAlgorithm.parameters = asn1_null;
981
982 /* encode request info by itself to calculate signature */
983 SecAsn1Item tbscert = {};
984 SEC_ASN1EncodeItem(poolp, &tbscert, &cert_tmpl.tbs, kSecAsn1TBSCertificateTemplate);
985
986 /* calculate signature */
987 uint8_t tbscert_hash[CC_SHA1_DIGEST_LENGTH];
427c49bc 988 CCDigest(kCCDigestSHA1, tbscert.Data, tbscert.Length, tbscert_hash);
b1ab9ed8
A
989 uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)];
990 size_t signature_length = sizeof(signature);
991 require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1,
992 tbscert_hash, sizeof(tbscert_hash), signature, &signature_length), out);
993
994 /* signature */
995 cert_tmpl.signature.Data = signature;
996 cert_tmpl.signature.Length = signature_length * 8;
997
998 /* encode cert */
999 SecAsn1Item signed_cert = {};
1000 require_quiet(SEC_ASN1EncodeItem(poolp, &signed_cert, &cert_tmpl,
1001 kSecAsn1SignedCertTemplate), out);
1002 cert = SecCertificateCreateWithBytes(kCFAllocatorDefault,
1003 signed_cert.Data, signed_cert.Length);
1004 }
1005out:
1006 if (poolp)
1007 PORT_FreeArena(poolp, PR_TRUE);
1008 CFReleaseSafe(pubkey_attrs);
1009 return cert;
1010}
1011
1012
1013SecCertificateRef
1014SecIdentitySignCertificate(SecIdentityRef issuer, CFDataRef serialno,
1015 SecKeyRef publicKey, CFTypeRef subject, CFTypeRef extensions)
1016{
1017 SecCertificateRef cert = NULL;
1018 SecKeyRef privateKey = NULL;
1019
1020 PRArenaPool *poolp = PORT_NewArena(1024);
1021 CFDictionaryRef pubkey_attrs = NULL;
1022 if (!poolp)
1023 return NULL;
1024
1025 NSS_Certificate cert_tmpl;
1026 memset(&cert_tmpl, 0, sizeof(cert_tmpl));
1027
1028 /* version */
1029 unsigned char version = 2;
1030 cert_tmpl.tbs.version.Length = sizeof(version);
1031 cert_tmpl.tbs.version.Data = &version;
1032
1033 /* serialno */
1034 cert_tmpl.tbs.serialNumber.Length = CFDataGetLength(serialno);
1035 cert_tmpl.tbs.serialNumber.Data = (uint8_t*)CFDataGetBytePtr(serialno);
1036
1037 /* subject/issuer */
1038 if (CFArrayGetTypeID() == CFGetTypeID(subject))
1039 cert_tmpl.tbs.subject.rdns = make_subject(poolp, (CFArrayRef)subject);
1040 else if (CFDataGetTypeID() == CFGetTypeID(subject)) {
1041 SecAsn1Item subject_item = { CFDataGetLength(subject), (uint8_t*)CFDataGetBytePtr(subject) };
1042 require_noerr_quiet(SEC_ASN1DecodeItem(poolp, &cert_tmpl.tbs.subject.rdns, kSecAsn1NameTemplate, &subject_item), out);
1043 } else
1044 goto out;
1045
1046 SecCertificateRef issuer_cert = NULL;
1047 require_noerr(SecIdentityCopyCertificate(issuer, &issuer_cert), out);
427c49bc 1048 CFDataRef issuer_name = SecCertificateCopySubjectSequence(issuer_cert);
b1ab9ed8
A
1049 SecAsn1Item issuer_item = { CFDataGetLength(issuer_name), (uint8_t*)CFDataGetBytePtr(issuer_name) };
1050 require_noerr_action_quiet(SEC_ASN1DecodeItem(poolp, &cert_tmpl.tbs.issuer.rdns,
1051 kSecAsn1NameTemplate, &issuer_item), out, CFReleaseNull(issuer_name));
1052 CFReleaseNull(issuer_name);
1053
1054 DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent(), &cert_tmpl.tbs.validity.notBefore.item);
1055 cert_tmpl.tbs.validity.notBefore.tag = SEC_ASN1_UTC_TIME;
1056 DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl.tbs.validity.notAfter.item);
1057 cert_tmpl.tbs.validity.notAfter.tag = SEC_ASN1_UTC_TIME;
1058
1059 /* extensions */
1060 if (extensions) {
1061 if (CFDataGetTypeID() == CFGetTypeID(extensions)) {
1062 SecAsn1Item requested_extensions = { CFDataGetLength(extensions), (uint8_t*)CFDataGetBytePtr(extensions) };
1063 //NSS_CertExtension **requested_extensions_decoded;
1064 require_noerr_quiet(SEC_ASN1DecodeItem(poolp, &cert_tmpl.tbs.extensions,
1065 kSecAsn1SequenceOfCertExtensionTemplate, &requested_extensions), out);
1066 } else if (CFDictionaryGetTypeID() == CFGetTypeID(extensions)) {
1067 cert_tmpl.tbs.extensions = extensions_from_parameters(poolp, extensions);
1068 }
1069 }
1070
1071 /* @@@ we only handle rsa keys */
1072 pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey);
1073 CFTypeRef key_type = CFDictionaryGetValue(pubkey_attrs, kSecAttrKeyType);
1074 if (key_type && CFEqual(key_type, kSecAttrKeyTypeRSA)) {
1075 /* public key data and algorithm */
1076 cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.algorithm = CSSMOID_RSA;
1077 cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.parameters = asn1_null;
1078
1079 CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData);
1080 cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey);
1081 cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey);
1082
1083 /* signature algorithm */
1084 cert_tmpl.tbs.signature.algorithm = CSSMOID_SHA1WithRSA;
1085 cert_tmpl.tbs.signature.parameters = asn1_null;
1086 cert_tmpl.signatureAlgorithm.algorithm = CSSMOID_SHA1WithRSA;
1087 cert_tmpl.signatureAlgorithm.parameters = asn1_null;
1088
1089 /* encode request info by itself to calculate signature */
1090 SecAsn1Item tbscert = {};
1091 SEC_ASN1EncodeItem(poolp, &tbscert, &cert_tmpl.tbs, kSecAsn1TBSCertificateTemplate);
1092
1093 /* calculate signature */
1094 uint8_t tbscert_hash[CC_SHA1_DIGEST_LENGTH];
427c49bc 1095 CCDigest(kCCDigestSHA1, tbscert.Data, tbscert.Length, tbscert_hash);
b1ab9ed8
A
1096 uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)];
1097 size_t signature_length = sizeof(signature);
1098
1099 require_noerr_quiet(SecIdentityCopyPrivateKey(issuer, &privateKey), out);
1100 require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1,
1101 tbscert_hash, sizeof(tbscert_hash), signature, &signature_length), out);
1102
1103 /* signature */
1104 cert_tmpl.signature.Data = signature;
1105 cert_tmpl.signature.Length = signature_length * 8;
1106
1107 /* encode cert */
1108 SecAsn1Item signed_cert = {};
1109 require_quiet(SEC_ASN1EncodeItem(poolp, &signed_cert, &cert_tmpl,
1110 kSecAsn1SignedCertTemplate), out);
1111 cert = SecCertificateCreateWithBytes(kCFAllocatorDefault,
1112 signed_cert.Data, signed_cert.Length);
1113 }
1114out:
1115 CFReleaseSafe(privateKey);
1116 if (poolp)
1117 PORT_FreeArena(poolp, PR_TRUE);
1118 CFReleaseSafe(pubkey_attrs);
1119 return cert;
1120}
1121
1122CFDataRef
1123SecGenerateCertificateRequestSubject(SecCertificateRef ca_certificate, CFArrayRef subject)
1124{
1125 CFMutableDataRef sequence = NULL;
1126 PRArenaPool *poolp = PORT_NewArena(1024);
1127 if (!poolp)
1128 return NULL;
1129
1130 /*
1131 Going agains the spec here:
1132
1133 3.2.3. GetCertInitial
1134
1135 The messageData for this type consists of a DER-encoded
1136 IssuerAndSubject (Section 3.2.3.1). The issuer is set to the
1137 issuerName from the certification authority from which we are issued
1138 certificates. The Subject is set to the SubjectName we used when
1139 requesting the certificate.
1140
1141 That clearly says use the issuer of the cert issuing certificate. Since
1142 it is combined with the subject of the to-be-issued certificate, that
1143 seems a mistake. If we take the subject of the issuer and the subject
1144 of the certificate we're interested in, we get the issuer and subject
1145 the certificate to be returned will have.
1146
1147 */
1148 CFDataRef issuer_sequence = SecCertificateCopySubjectSequence(ca_certificate);
1149 SecAsn1Item subject_item = { 0 };
1150 SecAsn1Item issuer_item = { CFDataGetLength(issuer_sequence), (uint8_t*)CFDataGetBytePtr(issuer_sequence) };
1151 NSS_Name nss_subject = { make_subject(poolp, subject) };
1152 require_quiet(SEC_ASN1EncodeItem(poolp, &subject_item, &nss_subject, kSecAsn1NameTemplate), out);
1153
1154 DERSize sequence_length = DERLengthOfLength(subject_item.Length + issuer_item.Length);
1155 DERSize seq_len_length = subject_item.Length + issuer_item.Length + 1 /* SEQUENCE */ +
1156 sequence_length;
1157 sequence = CFDataCreateMutable(kCFAllocatorDefault, 0);
1158 CFDataSetLength(sequence, seq_len_length);
1159 uint8_t *sequence_ptr = CFDataGetMutableBytePtr(sequence);
1160 *sequence_ptr++ = 0x30; //ASN1_CONSTR_SEQUENCE;
1161 require_noerr_quiet(DEREncodeLength(subject_item.Length + issuer_item.Length, sequence_ptr, &sequence_length), out);
1162 sequence_ptr += sequence_length;
1163 memcpy(sequence_ptr, issuer_item.Data, issuer_item.Length);
1164 memcpy(sequence_ptr + issuer_item.Length, subject_item.Data, subject_item.Length);
1165
1166out:
1167 CFReleaseSafe(issuer_sequence);
1168 if (poolp)
1169 PORT_FreeArena(poolp, PR_TRUE);
1170 return sequence;
1171}