2 * Copyright (c) 2008-2009,2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <libDER/oids.h>
26 #include <libDER/DER_Encode.h>
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>
36 #include <TargetConditionals.h>
37 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
39 OSStatus
SecCmsArraySortByDER(void **objs
, const SecAsn1Template
*objtemplate
, void **objs2
);
42 #include <security_smime/cmspriv.h>
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>
54 #include <CommonCrypto/CommonDigestSPI.h>
55 #include <CoreFoundation/CFNumber.h>
56 #include <CoreFoundation/CFString.h>
58 #include <utilities/SecCFWrappers.h>
60 #include <AssertMacros.h>
62 #include "SecCertificateRequest.h"
64 CFTypeRef kSecOidCommonName
= CFSTR("CN");
65 CFTypeRef kSecOidCountryName
= CFSTR("C");
66 CFTypeRef kSecOidStateProvinceName
= CFSTR("ST");
67 CFTypeRef kSecOidLocalityName
= CFSTR("L");
68 CFTypeRef kSecOidOrganization
= CFSTR("O");
69 CFTypeRef kSecOidOrganizationalUnit
= CFSTR("OU");
70 //CFTypeRef kSecOidEmailAddress = CFSTR("1.2.840.113549.1.9.1");
71 // keep natural order: C > ST > L > O > OU > CN > Email
73 const unsigned char SecASN1PrintableString
= SEC_ASN1_PRINTABLE_STRING
;
74 const unsigned char SecASN1UTF8String
= SEC_ASN1_UTF8_STRING
;
76 static uint8_t * mod128_oid_encoding_ptr(uint8_t *ptr
, uint32_t src
, bool final
)
79 ptr
= mod128_oid_encoding_ptr(ptr
, src
/ 128, false);
81 unsigned char octet
= src
% 128;
89 static uint8_t * oid_der_data(PRArenaPool
*poolp
, CFStringRef oid_string
, size_t *oid_data_len
)
91 /* estimate encoded length from base 10 (4 bits) to base 128 (7 bits) */
92 size_t tmp_oid_length
= ((CFStringGetLength(oid_string
) * 4) / 7) + 1;
93 uint8_t *tmp_oid_data
= PORT_ArenaAlloc(poolp
, tmp_oid_length
);
94 uint8_t *tmp_oid_data_ptr
= tmp_oid_data
;
96 CFArrayRef oid
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
,
97 oid_string
, CFSTR("."));
98 CFIndex i
= 0, count
= CFArrayGetCount(oid
);
99 SInt32 first_digit
= 0, digit
;
100 for (i
= 0; i
< count
; i
++) {
101 CFStringRef oid_octet
= CFArrayGetValueAtIndex(oid
, i
);
102 SInt32 oid_octet_int_value
= CFStringGetIntValue(oid_octet
);
103 require(abs(oid_octet_int_value
) != INT32_MAX
, out
);
105 first_digit
= oid_octet_int_value
;
108 digit
= 40 * first_digit
+ oid_octet_int_value
;
110 digit
= oid_octet_int_value
;
111 tmp_oid_data_ptr
= mod128_oid_encoding_ptr(tmp_oid_data_ptr
, digit
, true);
116 *oid_data_len
= tmp_oid_data_ptr
- tmp_oid_data
;
125 Get challenge password conversion and apply this:
127 ASCII ? => PrintableString subset: [A-Za-z0-9 '()+,-./:=?] ?
129 PrintableString > IA5String > UTF8String
131 Consider using IA5String for email address
134 static inline bool printable_string(CFStringRef string
)
138 CFCharacterSetRef printable_charset
=
139 CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
,
140 CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
141 "abcdefghijklmnopqrstuvwxyz"
142 "0123456789 '()+,-./:=?"));
143 CFCharacterSetRef not_printable_charset
=
144 CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, printable_charset
);
146 if (CFStringFindCharacterFromSet(string
, not_printable_charset
,
147 CFRangeMake(0, CFStringGetLength(string
)), 0, &found
))
150 CFReleaseSafe(printable_charset
);
151 CFReleaseSafe(not_printable_charset
);
156 static bool make_nss_atv(PRArenaPool
*poolp
,
157 const void * oid
, const void * value
, const unsigned char type_in
, NSS_ATV
*nss_atv
)
161 unsigned char type
= type_in
;
162 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
163 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
164 kCFStringEncodingUTF8
);
165 buffer
= PORT_ArenaAlloc(poolp
, length
);
166 /* TODO: Switch to using CFStringGetBytes,since this code will do the wrong thing for embedded 0's */
167 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingASCII
)) {
168 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
170 if (type
&& type
!= SecASN1UTF8String
)
172 type
= SecASN1UTF8String
;
175 if (!type
|| type
== SecASN1PrintableString
) {
176 if (!printable_string(value
))
177 type
= SEC_ASN1_IA5_STRING
;
179 type
= SEC_ASN1_PRINTABLE_STRING
;
182 length
= strlen(buffer
);
184 else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
185 /* will remain valid for the duration of the operation, still maybe copy into pool */
186 length
= CFDataGetLength(value
);
187 buffer
= (char *)CFDataGetBytePtr(value
);
189 size_t oid_length
= 0;
190 uint8_t *oid_data
= NULL
;
191 if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
192 if (CFEqual(kSecOidCommonName
, oid
)) {
193 oid_length
= oidCommonName
.length
; oid_data
= oidCommonName
.data
;
194 } else if (CFEqual(kSecOidCountryName
, oid
)) {
195 oid_length
= oidCountryName
.length
; oid_data
= oidCountryName
.data
;
196 } else if (CFEqual(kSecOidStateProvinceName
, oid
)) {
197 oid_length
= oidStateOrProvinceName
.length
; oid_data
= oidStateOrProvinceName
.data
;
198 } else if (CFEqual(kSecOidLocalityName
, oid
)) {
199 oid_length
= oidLocalityName
.length
; oid_data
= oidLocalityName
.data
;
200 } else if (CFEqual(kSecOidOrganization
, oid
)) {
201 oid_length
= oidOrganizationName
.length
; oid_data
= oidOrganizationName
.data
;
202 } else if (CFEqual(kSecOidOrganizationalUnit
, oid
)) {
203 oid_length
= oidOrganizationalUnitName
.length
; oid_data
= oidOrganizationalUnitName
.data
;
205 oid_data
= oid_der_data(poolp
, oid
, &oid_length
);
206 require(oid_data
, out
);
208 } else if (CFGetTypeID(oid
) == CFDataGetTypeID()) {
209 /* will remain valid for the duration of the operation, still maybe copy into pool */
210 oid_length
= CFDataGetLength(oid
);
211 oid_data
= (uint8_t *)CFDataGetBytePtr(oid
);
213 NSS_ATV stage_nss_atv
= { { oid_length
, oid_data
},
214 { { length
, (uint8_t*)buffer
}, type
} };
215 *nss_atv
= stage_nss_atv
;
221 static NSS_RDN
**make_subject(PRArenaPool
*poolp
, CFArrayRef subject
)
225 CFIndex rdn_ix
, rdn_count
= CFArrayGetCount(subject
);
226 NSS_RDN
**rdnps
= PORT_ArenaZNewArray(poolp
, NSS_RDN
*, rdn_count
+ 1);
227 NSS_RDN
*rdns
= PORT_ArenaZNewArray(poolp
, NSS_RDN
, rdn_count
);
228 for (rdn_ix
= 0; rdn_ix
< rdn_count
; rdn_ix
++) {
229 rdnps
[rdn_ix
] = &rdns
[rdn_ix
];
230 CFArrayRef rdn
= CFArrayGetValueAtIndex(subject
, rdn_ix
);
231 CFIndex atv_ix
, atv_count
= CFArrayGetCount(rdn
);
232 rdns
[rdn_ix
].atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
*, atv_count
+ 1);
233 NSS_ATV
*atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
, atv_count
);
234 for (atv_ix
= 0; atv_ix
< atv_count
; atv_ix
++) {
235 rdns
[rdn_ix
].atvs
[atv_ix
] = &atvs
[atv_ix
];
236 CFArrayRef atv
= CFArrayGetValueAtIndex(rdn
, atv_ix
);
237 if ((CFArrayGetCount(atv
) != 2)
238 || !make_nss_atv(poolp
, CFArrayGetValueAtIndex(atv
, 0),
239 CFArrayGetValueAtIndex(atv
, 1), 0, &atvs
[atv_ix
]))
246 struct make_general_names_context
{
253 static void make_general_names(const void *key
, const void *value
, void *context
)
255 struct make_general_names_context
*gn
= (struct make_general_names_context
*)context
;
257 CFArrayRef gn_values
= NULL
;
258 CFStringRef gn_value
= NULL
;
259 CFIndex entry_ix
, entry_count
= 0;
260 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
261 gn_values
= (CFArrayRef
)value
;
262 entry_count
= CFArrayGetCount(value
);
263 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
264 gn_value
= (CFStringRef
)value
;
268 require(entry_count
> 0, out
);
271 require(CFGetTypeID(key
) == CFStringGetTypeID(), out
);
273 if (!gn
->names
|| (gn
->count
== gn
->capacity
)) {
274 uint32_t capacity
= gn
->capacity
;
280 void * new_array
= PORT_ArenaZNewArray(gn
->poolp
, SecAsn1Item
, capacity
);
282 memcpy(new_array
, gn
->names
, gn
->capacity
);
283 gn
->names
= new_array
;
284 gn
->capacity
= capacity
;
287 NSS_GeneralName general_name_item
= { { }, -1 };
288 if (kCFCompareEqualTo
== CFStringCompare(CFSTR("dNSName"), key
, kCFCompareCaseInsensitive
))
289 general_name_item
.tag
= NGT_DNSName
;
290 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("rfc822Name"), key
, kCFCompareCaseInsensitive
))
291 general_name_item
.tag
= NGT_RFC822Name
;
292 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("uniformResourceIdentifier"), key
, kCFCompareCaseInsensitive
))
293 general_name_item
.tag
= NGT_URI
;
294 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("ntPrincipalName"), key
, kCFCompareCaseInsensitive
))
297 NT Principal in SubjectAltName is defined in the context of Smartcards:
299 http://www.oid-info.com/get/1.3.6.1.4.1.311.20.2.3
300 http://support.microsoft.com/default.aspx?scid=kb;en-us;281245
302 Subject Alternative Name = Other Name: Principal Name= (UPN). For example:
304 The UPN OtherName OID is : "1.3.6.1.4.1.311.20.2.3"
305 The UPN OtherName value: Must be ASN1-encoded UTF8 string
306 Subject = Distinguished name of user. This field is a mandatory extension, but the population of this field is optional.
309 /* OtherName ::= SEQUENCE {
310 type-id OBJECT IDENTIFIER,
311 value [0] EXPLICIT ANY DEFINED BY type-id
313 uint8_t nt_principal_oid
[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03 };
317 } nt_principal_other_name
;
318 nt_principal_other_name name
= {};
320 const SecAsn1Template my_other_name_template
[] = {
322 0, NULL
, sizeof(nt_principal_other_name
) },
323 { SEC_ASN1_OBJECT_ID
,
324 offsetof(nt_principal_other_name
,typeId
), },
325 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| 0, offsetof(nt_principal_other_name
,value
), kSecAsn1UTF8StringTemplate
, },
328 const SecAsn1Template my_other_name_template_cons
[] = {
329 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| NGT_OtherName
,
330 0, my_other_name_template
, sizeof(nt_principal_other_name
) }
336 require(gn_value
, out
);
337 require(CFGetTypeID(gn_value
) == CFStringGetTypeID(), out
);
338 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
339 kCFStringEncodingUTF8
);
340 buffer
= PORT_ArenaAlloc(gn
->poolp
, length
);
341 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
344 name
.typeId
.Length
= sizeof(nt_principal_oid
);
345 name
.typeId
.Data
= nt_principal_oid
;
346 name
.value
.Length
= strlen(buffer
);
347 name
.value
.Data
= (uint8_t*)buffer
;
348 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &name
, my_other_name_template_cons
);
351 /* We already encoded the value for the general name */
358 for (entry_ix
= 0; entry_ix
< entry_count
; entry_ix
++) {
359 CFTypeRef entry_value
= CFArrayGetValueAtIndex(gn_values
, entry_ix
);
360 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
)entry_value
),
361 kCFStringEncodingUTF8
); /* we only allow ASCII => only expect IA5Strings */
362 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
363 require(CFStringGetCString((CFStringRef
)entry_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
364 general_name_item
.item
.Data
= (uint8_t*)buffer
;
365 general_name_item
.item
.Length
= strlen(buffer
);
366 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
369 } else if (gn_value
) {
370 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(gn_value
),
371 kCFStringEncodingUTF8
);
372 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
373 require(CFStringGetCString(gn_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
374 general_name_item
.item
.Data
= (uint8_t*)buffer
;
375 general_name_item
.item
.Length
= strlen(buffer
);
376 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
383 static SecAsn1Item
make_subjectAltName_extension(PRArenaPool
*poolp
, CFDictionaryRef subjectAltNames
)
385 SecAsn1Item subjectAltExt
= {};
387 struct make_general_names_context context
= { poolp
, NULL
, 0 };
388 CFDictionaryApplyFunction(subjectAltNames
, make_general_names
, &context
);
390 // all general names in a sequence:
392 SecAsn1Item
**general_names
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, context
.count
+ 1);
393 for (ix
= 0; ix
< context
.count
; ix
++)
394 general_names
[ix
] = &context
.names
[ix
];
395 NSS_GeneralNames gnames
= { general_names
};
396 SEC_ASN1EncodeItem(poolp
, &subjectAltExt
, &gnames
, kSecAsn1GeneralNamesTemplate
);
398 return subjectAltExt
;
401 CFTypeRef kSecCSRChallengePassword
= CFSTR("csrChallengePassword");
402 CFTypeRef kSecSubjectAltName
= CFSTR("subjectAltName");
403 CFTypeRef kSecCertificateKeyUsage
= CFSTR("keyUsage");
404 CFTypeRef kSecCSRBasicContraintsPathLen
= CFSTR("basicConstraints");
405 CFTypeRef kSecCertificateExtensions
= CFSTR("certificateExtensions");
406 CFTypeRef kSecCertificateExtensionsEncoded
= CFSTR("certificateExtensionsEncoded");
408 static const uint8_t pkcs9ExtensionsRequested
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 14 };
409 static const uint8_t pkcs9ChallengePassword
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 7 };
411 static const uint8_t encoded_asn1_true
= 0xFF;
412 static const SecAsn1Item asn1_true
=
413 { sizeof(encoded_asn1_true
), (uint8_t*)&encoded_asn1_true
};
415 __unused
static inline uint32_t highest_bit(uint32_t n
)
417 return ((n
) >> 16 ? ((n
)>>=16, 16) : 0) + \
418 ((n
) >> 8 ? ((n
)>>=8, 8) : 0) + \
419 ((n
) >> 4 ? ((n
)>>=4, 4) : 0) + \
420 ((n
) >> 2 ? ((n
)>>=2, 2) : 0) + \
421 ((n
) >> 1 ? ((n
)>>=1, 1) : 0) + \
425 struct add_custom_extension_args
{
427 NSS_CertExtension
*csr_extension
;
428 uint32_t num_extensions
;
429 uint32_t max_extensions
;
433 static void add_custom_extension(const void *key
, const void *value
, void *context
)
435 struct add_custom_extension_args
*args
= (struct add_custom_extension_args
*)context
;
438 require(args
->num_extensions
< args
->max_extensions
, out
);
440 uint8_t * der_data
= oid_der_data(args
->poolp
, key
, &der_data_len
);
441 SecAsn1Item encoded_value
= {};
443 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
444 if (!args
->encodeData
) {
447 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
), kCFStringEncodingUTF8
);
448 char *buffer
= (char *)PORT_ArenaZNewArray(args
->poolp
, uint8_t, buffer_size
);
449 if (!CFStringGetCString(value
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
452 SecAsn1Item buffer_item
= { strlen(buffer
), (uint8_t*)buffer
};
453 SEC_ASN1EncodeItem(args
->poolp
, &encoded_value
, &buffer_item
, kSecAsn1UTF8StringTemplate
);
454 } else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
455 if (args
->encodeData
) {
456 SecAsn1Item data_item
= { CFDataGetLength(value
), (uint8_t*)CFDataGetBytePtr(value
) };
457 SEC_ASN1EncodeItem(args
->poolp
, &encoded_value
, &data_item
, kSecAsn1OctetStringTemplate
);
460 encoded_value
.Length
= CFDataGetLength(value
);
461 encoded_value
.Data
= (uint8_t*)CFDataGetBytePtr(value
);
467 if (der_data
&& encoded_value
.Length
) {
468 args
->csr_extension
[args
->num_extensions
].value
= encoded_value
;
469 args
->csr_extension
[args
->num_extensions
].extnId
.Length
= der_data_len
;
470 args
->csr_extension
[args
->num_extensions
].extnId
.Data
= der_data
;
471 args
->num_extensions
++;
479 extensions_from_parameters(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
481 uint32_t num_extensions
= 0, max_extensions
= 10;
482 NSS_CertExtension
**csr_extensions
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
*, max_extensions
+ 1); /* NULL terminated array */
483 NSS_CertExtension
*csr_extension
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
, max_extensions
);
485 CFNumberRef basic_contraints_num
= CFDictionaryGetValue(parameters
, kSecCSRBasicContraintsPathLen
);
486 if (basic_contraints_num
) {
487 NSS_BasicConstraints basic_contraints
= { asn1_true
, {} };
490 int basic_contraints_path_len
= 0;
491 require(CFNumberGetValue(basic_contraints_num
, kCFNumberIntType
, &basic_contraints_path_len
), out
);
492 if (basic_contraints_path_len
>= 0 && basic_contraints_path_len
< 256) {
493 path_len
= (uint8_t)basic_contraints_path_len
;
494 basic_contraints
.pathLenConstraint
.Length
= sizeof(path_len
);
495 basic_contraints
.pathLenConstraint
.Data
= &path_len
;
498 csr_extension
[num_extensions
].extnId
.Data
= oidBasicConstraints
.data
;
499 csr_extension
[num_extensions
].extnId
.Length
= oidBasicConstraints
.length
;
500 csr_extension
[num_extensions
].critical
= asn1_true
;
502 SEC_ASN1EncodeItem(poolp
, &csr_extension
[num_extensions
].value
, &basic_contraints
,
503 kSecAsn1BasicConstraintsTemplate
);
504 require(num_extensions
++ < max_extensions
, out
);
507 CFDictionaryRef subject_alternate_names
= CFDictionaryGetValue(parameters
, kSecSubjectAltName
);
508 if (subject_alternate_names
) {
509 require(CFGetTypeID(subject_alternate_names
) == CFDictionaryGetTypeID(), out
);
510 csr_extension
[num_extensions
].value
= make_subjectAltName_extension(poolp
, subject_alternate_names
);
511 /* set up subjectAltName cert request value */
512 csr_extension
[num_extensions
].extnId
.Length
= oidSubjectAltName
.length
;
513 csr_extension
[num_extensions
].extnId
.Data
= oidSubjectAltName
.data
;
514 require(num_extensions
++ < max_extensions
, out
);
517 CFNumberRef key_usage_requested
= CFDictionaryGetValue(parameters
, kSecCertificateKeyUsage
);
518 SecAsn1Item key_usage_asn1_value
= { 0 };
519 if (key_usage_requested
) {
521 require(CFNumberGetValue(key_usage_requested
, kCFNumberIntType
, &key_usage_value
), out
);
522 if (key_usage_value
> 0) {
523 uint32_t key_usage_value_be
= 0, key_usage_mask
= 1<<31;
524 uint32_t key_usage_value_max_bitlen
= 9, key_usage_value_bitlen
= 0;
525 while(key_usage_value_max_bitlen
) {
526 if (key_usage_value
& 1) {
527 key_usage_value_be
|= key_usage_mask
;
528 key_usage_value_bitlen
= 10 - key_usage_value_max_bitlen
;
530 key_usage_value
>>= 1;
531 key_usage_value_max_bitlen
--;
532 key_usage_mask
>>= 1;
535 SecAsn1Item key_usage_input
= { key_usage_value_bitlen
,
536 ((uint8_t*)&key_usage_value_be
) + 3 - (key_usage_value_bitlen
>> 3) };
537 SEC_ASN1EncodeItem(poolp
, &key_usage_asn1_value
, &key_usage_input
, kSecAsn1BitStringTemplate
);
539 csr_extension
[num_extensions
].extnId
.Data
= oidKeyUsage
.data
;
540 csr_extension
[num_extensions
].extnId
.Length
= oidKeyUsage
.length
;
541 csr_extension
[num_extensions
].critical
= asn1_true
;
542 csr_extension
[num_extensions
].value
= key_usage_asn1_value
;
543 require(num_extensions
++ < max_extensions
, out
);
547 CFDictionaryRef custom_extension_requested
= CFDictionaryGetValue(parameters
, kSecCertificateExtensions
);
548 if (custom_extension_requested
) {
549 require(CFGetTypeID(custom_extension_requested
) == CFDictionaryGetTypeID(), out
);
550 struct add_custom_extension_args args
= {
557 CFDictionaryApplyFunction(custom_extension_requested
, add_custom_extension
, &args
);
558 num_extensions
= args
.num_extensions
;
561 CFDictionaryRef custom_encoded_extension_requested
= CFDictionaryGetValue(parameters
, kSecCertificateExtensionsEncoded
);
562 if (custom_encoded_extension_requested
) {
563 require(CFGetTypeID(custom_encoded_extension_requested
) == CFDictionaryGetTypeID(), out
);
564 struct add_custom_extension_args args
= {
571 CFDictionaryApplyFunction(custom_encoded_extension_requested
, add_custom_extension
, &args
);
572 num_extensions
= args
.num_extensions
;
575 /* extensions requested (subjectAltName, keyUsage) sequence of extension sequences */
577 for (ix
= 0; ix
< num_extensions
; ix
++)
578 csr_extensions
[ix
] = csr_extension
[ix
].extnId
.Length
? &csr_extension
[ix
] : NULL
;
581 return csr_extensions
;
587 NSS_Attribute
**nss_attributes_from_parameters_dict(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
589 /* A challenge-password attribute must have a single attribute value.
591 ChallengePassword attribute values generated in accordance with this
592 version of this document SHOULD use the PrintableString encoding
593 whenever possible. If internationalization issues make this
594 impossible, the UTF8String alternative SHOULD be used. PKCS #9-
595 attribute processing systems MUST be able to recognize and process
596 all string types in DirectoryString values.
598 Upperbound of 255 defined for all PKCS#9 attributes.
600 pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840)
601 rsadsi(113549) pkcs(1) 9}
602 pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7}
607 uint32_t num_attrs
= 0;
609 CFStringRef challenge
= CFDictionaryGetValue(parameters
, kSecCSRChallengePassword
);
610 NSS_Attribute challenge_password_attr
= {};
612 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(challenge
),
613 kCFStringEncodingUTF8
); /* we only allow UTF8 or ASCII */
614 char *buffer
= (char *)PORT_ArenaZNewArray(poolp
, uint8_t, buffer_size
);
616 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingASCII
)) {
617 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
621 if (!printable_string(challenge
))
624 SecAsn1Item
*challenge_password_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
625 SecAsn1Item challenge_password_raw
= { strlen(buffer
), (uint8_t*)buffer
};
626 SEC_ASN1EncodeItem(poolp
, challenge_password_value
, &challenge_password_raw
,
627 utf8
? kSecAsn1UTF8StringTemplate
: kSecAsn1PrintableStringTemplate
);
628 SecAsn1Item
**challenge_password_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
629 challenge_password_values
[0] = challenge_password_value
;
630 challenge_password_attr
.attrType
.Length
= sizeof(pkcs9ChallengePassword
);
631 challenge_password_attr
.attrType
.Data
= (uint8_t*)&pkcs9ChallengePassword
;
632 challenge_password_attr
.attrValue
= challenge_password_values
;
636 NSS_CertExtension
**extensions
= extensions_from_parameters(poolp
, parameters
);
637 NSS_Attribute extensions_requested_attr
= {};
639 SecAsn1Item
*extensions_requested_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
640 SEC_ASN1EncodeItem(poolp
, extensions_requested_value
, &extensions
, kSecAsn1SequenceOfCertExtensionTemplate
);
641 SecAsn1Item
**extensions_requested_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
642 extensions_requested_values
[0] = extensions_requested_value
;
643 extensions_requested_values
[1] = NULL
;
644 extensions_requested_attr
.attrType
.Length
= sizeof(pkcs9ExtensionsRequested
);
645 extensions_requested_attr
.attrType
.Data
= (uint8_t*)pkcs9ExtensionsRequested
;
646 extensions_requested_attr
.attrValue
= extensions_requested_values
;
650 NSS_Attribute
**attributes_ptr
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
*, num_attrs
+ 1);
651 NSS_Attribute
*attributes
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
, num_attrs
);
652 if (challenge_password_attr
.attrType
.Length
) {
654 attributes
[num_attrs
] = challenge_password_attr
;
655 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
657 if (extensions_requested_attr
.attrType
.Length
) {
659 attributes
[num_attrs
] = extensions_requested_attr
;
660 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
662 return attributes_ptr
;
669 static const uint8_t encoded_null
[2] = { SEC_ASN1_NULL
, 0 };
670 static const SecAsn1Item asn1_null
= { sizeof(encoded_null
), (uint8_t*)encoded_null
};
672 CFDataRef
SecGenerateCertificateRequestWithParameters(SecRDN
*subject
,
673 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
675 CFDataRef csr
= NULL
;
676 PRArenaPool
*poolp
= PORT_NewArena(1024);
677 CFDictionaryRef pubkey_attrs
= NULL
;
682 NSSCertRequest certReq
;
683 memset(&certReq
, 0, sizeof(certReq
));
686 unsigned char version
= 0;
687 certReq
.reqInfo
.version
.Length
= sizeof(version
);
688 certReq
.reqInfo
.version
.Data
= &version
;
691 unsigned atv_num
= 0, num
= 0;
694 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
695 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++)
697 atv_num
++; /* one more */
700 const unsigned min1atv_num
= atv_num
> 0 ? atv_num
: 1;
701 const unsigned min1num
= num
> 0 ? num
: 1;
702 NSS_ATV atvs
[min1atv_num
];
703 NSS_ATV
*atvps
[min1atv_num
];
704 NSS_RDN rdns
[min1num
];
705 NSS_RDN
*rdnps
[num
+1];
707 unsigned rdn_num
= 0;
708 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
709 rdns
[rdn_num
].atvs
= &atvps
[atv_num
];
710 rdnps
[rdn_num
] = &rdns
[rdn_num
];
712 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++) {
713 if (!make_nss_atv(poolp
, one_atv
->oid
, one_atv
->value
,
714 one_atv
->type
, &atvs
[atv_num
]))
716 atvps
[atv_num
] = &atvs
[atv_num
];
719 atvps
[atv_num
++] = NULL
;
721 rdnps
[rdn_num
] = NULL
;
722 certReq
.reqInfo
.subject
.rdns
= rdnps
;
724 /* public key info */
725 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
726 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
727 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
729 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
730 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
731 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
732 size_t signature_length
= sizeof(signature
);
734 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
735 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
737 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
738 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
740 /* encode request info by itself to calculate signature */
741 SecAsn1Item reqinfo
= {};
742 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
744 /* calculate signature */
745 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
746 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
747 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
748 reqinfo_hash
, sizeof(reqinfo_hash
), signature
, &signature_length
), out
);
750 /* signature and info */
751 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha1Rsa
.length
;
752 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha1Rsa
.data
;
753 certReq
.signatureAlgorithm
.parameters
= asn1_null
;
754 certReq
.signature
.Data
= signature
;
755 certReq
.signature
.Length
= signature_length
* 8;
758 SecAsn1Item cert_request
= {};
759 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
760 kSecAsn1CertRequestTemplate
), out
);
761 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
765 PORT_FreeArena(poolp
, PR_TRUE
);
766 CFReleaseSafe(pubkey_attrs
);
770 CFDataRef
SecGenerateCertificateRequest(CFArrayRef subject
,
771 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
773 CFDataRef csr
= NULL
;
774 PRArenaPool
*poolp
= PORT_NewArena(1024);
775 CFDictionaryRef pubkey_attrs
= NULL
;
780 NSSCertRequest certReq
;
781 memset(&certReq
, 0, sizeof(certReq
));
784 unsigned char version
= 0;
785 certReq
.reqInfo
.version
.Length
= sizeof(version
);
786 certReq
.reqInfo
.version
.Data
= &version
;
789 certReq
.reqInfo
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
791 /* public key info */
792 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
793 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
794 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
796 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
797 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
798 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
799 size_t signature_length
= sizeof(signature
);
801 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
802 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
804 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
805 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
807 /* encode request info by itself to calculate signature */
808 SecAsn1Item reqinfo
= {};
809 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
811 /* calculate signature */
812 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
813 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, reqinfo
.Length
, reqinfo_hash
);
814 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
815 reqinfo_hash
, sizeof(reqinfo_hash
), signature
, &signature_length
), out
);
817 /* signature and info */
818 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha1Rsa
.length
;
819 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha1Rsa
.data
;
820 certReq
.signatureAlgorithm
.parameters
= asn1_null
;
821 certReq
.signature
.Data
= signature
;
822 certReq
.signature
.Length
= signature_length
* 8;
825 SecAsn1Item cert_request
= {};
826 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
827 kSecAsn1CertRequestTemplate
), out
);
828 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
832 PORT_FreeArena(poolp
, PR_TRUE
);
833 CFReleaseSafe(pubkey_attrs
);
837 bool SecVerifyCertificateRequest(CFDataRef csr
, SecKeyRef
*publicKey
,
838 CFStringRef
*challenge
, CFDataRef
*subject
, CFDataRef
*extensions
)
840 PRArenaPool
*poolp
= PORT_NewArena(1024);
841 SecKeyRef candidatePublicKey
= NULL
;
843 NSSCertRequest certReq
;
844 memset(&certReq
, 0, sizeof(certReq
));
845 SecAsn1Item csr_item
= { CFDataGetLength(csr
), (uint8_t*)CFDataGetBytePtr(csr
) };
846 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &certReq
, kSecAsn1CertRequestTemplate
,
849 /* signature and info */
850 require(certReq
.signatureAlgorithm
.algorithm
.Length
== oidSha1Rsa
.length
, out
);
851 require_noerr(memcmp(oidSha1Rsa
.data
, certReq
.signatureAlgorithm
.algorithm
.Data
,
852 oidSha1Rsa
.length
), out
);
853 require(certReq
.signatureAlgorithm
.parameters
.Length
== asn1_null
.Length
, out
);
854 require_noerr(memcmp(asn1_null
.Data
, certReq
.signatureAlgorithm
.parameters
.Data
,
855 asn1_null
.Length
), out
);
857 /* encode request info by itself to calculate signature */
858 SecAsn1Item reqinfo
= {};
859 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
861 /* calculate signature */
862 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
863 require(reqinfo
.Length
<=UINT32_MAX
, out
);
864 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
866 /* @@@ check for version 0 */
868 require(candidatePublicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
869 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
,
870 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
/ 8,
871 kSecKeyEncodingPkcs1
), out
);
873 require_noerr_quiet(SecKeyRawVerify(candidatePublicKey
, kSecPaddingPKCS1SHA1
,
874 reqinfo_hash
, sizeof(reqinfo_hash
),
875 certReq
.signature
.Data
, certReq
.signature
.Length
/ 8), out
);
877 SecAsn1Item subject_item
= { 0 }, extensions_item
= { 0 }, challenge_item
= { 0 };
878 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
,
879 &certReq
.reqInfo
.subject
, kSecAsn1NameTemplate
), out
);
881 if (*certReq
.reqInfo
.attributes
) {
883 for (ix
= 0; certReq
.reqInfo
.attributes
[ix
]; ix
++) {
884 NSS_Attribute
*attr
= certReq
.reqInfo
.attributes
[ix
];
885 if ( (sizeof(pkcs9ChallengePassword
) == attr
->attrType
.Length
) &&
886 !memcmp(pkcs9ChallengePassword
, attr
->attrType
.Data
, sizeof(pkcs9ChallengePassword
)))
887 challenge_item
= *attr
->attrValue
[0];
888 else if ( (sizeof(pkcs9ExtensionsRequested
) == attr
->attrType
.Length
) &&
889 !memcmp(pkcs9ExtensionsRequested
, attr
->attrType
.Data
, sizeof(pkcs9ExtensionsRequested
)))
890 extensions_item
= *attr
->attrValue
[0];
894 if (subject
&& subject_item
.Length
)
895 *subject
= CFDataCreate(kCFAllocatorDefault
, subject_item
.Data
, subject_item
.Length
);
896 if (extensions
&& extensions_item
.Length
)
897 *extensions
= CFDataCreate(kCFAllocatorDefault
, extensions_item
.Data
, extensions_item
.Length
);
898 if (challenge
&& challenge_item
.Length
) {
899 SecAsn1Item string
= { 0 };
900 SECStatus rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1UTF8StringTemplate
, &challenge_item
);
902 rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1PrintableStringTemplate
, &challenge_item
);
904 *challenge
= CFStringCreateWithBytes(kCFAllocatorDefault
, string
.Data
, string
.Length
, kCFStringEncodingUTF8
, false);
909 *publicKey
= candidatePublicKey
;
910 candidatePublicKey
= NULL
;
914 CFReleaseSafe(candidatePublicKey
);
916 PORT_FreeArena(poolp
, PR_TRUE
);
920 #define HIDIGIT(v) (((v) / 10) + '0')
921 #define LODIGIT(v) (((v) % 10) + '0')
924 DER_CFDateToUTCTime(PRArenaPool
*poolp
, CFAbsoluteTime date
, SecAsn1Item
* utcTime
)
928 utcTime
->Length
= 13;
929 utcTime
->Data
= d
= PORT_ArenaAlloc(poolp
, 13);
940 if (!CFCalendarDecomposeAbsoluteTime(SecCFCalendarGetZulu(), date
, "yMdHms", &year
, &month
, &day
, &hour
, &minute
, &second
))
944 /* UTC time does not handle the years before 1950 */
948 /* remove the century since it's added to the year by the
949 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
952 d
[0] = HIDIGIT(year
);
953 d
[1] = LODIGIT(year
);
954 d
[2] = HIDIGIT(month
);
955 d
[3] = LODIGIT(month
);
958 d
[6] = HIDIGIT(hour
);
959 d
[7] = LODIGIT(hour
);
960 d
[8] = HIDIGIT(minute
);
961 d
[9] = LODIGIT(minute
);
962 d
[10] = HIDIGIT(second
);
963 d
[11] = LODIGIT(second
);
969 SecGenerateSelfSignedCertificate(CFArrayRef subject
, CFDictionaryRef parameters
,
970 SecKeyRef publicKey
, SecKeyRef privateKey
)
972 SecCertificateRef cert
= NULL
;
973 PRArenaPool
*poolp
= PORT_NewArena(1024);
974 CFDictionaryRef pubkey_attrs
= NULL
;
978 NSS_Certificate cert_tmpl
;
979 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
982 unsigned char version
= 2;
983 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
984 cert_tmpl
.tbs
.version
.Data
= &version
;
987 unsigned char serialNumber
= 1;
988 cert_tmpl
.tbs
.serialNumber
.Length
= sizeof(serialNumber
);
989 cert_tmpl
.tbs
.serialNumber
.Data
= &serialNumber
;
992 cert_tmpl
.tbs
.issuer
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
993 cert_tmpl
.tbs
.subject
.rdns
= cert_tmpl
.tbs
.issuer
.rdns
;
995 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
996 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
997 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
998 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
1001 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, parameters
);
1003 /* @@@ we only handle rsa keys */
1004 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
1005 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
1006 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
1007 /* public key data and algorithm */
1008 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
1009 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
1011 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
1012 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
1013 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
1015 /* signature algorithm */
1016 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
1017 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
1018 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
1019 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
1021 /* encode request info by itself to calculate signature */
1022 SecAsn1Item tbscert
= {};
1023 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
1025 /* calculate signature */
1026 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
1027 CCDigest(kCCDigestSHA1
, tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
1028 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
1029 size_t signature_length
= sizeof(signature
);
1030 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
1031 tbscert_hash
, sizeof(tbscert_hash
), signature
, &signature_length
), out
);
1034 cert_tmpl
.signature
.Data
= signature
;
1035 cert_tmpl
.signature
.Length
= signature_length
* 8;
1038 SecAsn1Item signed_cert
= {};
1039 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1040 kSecAsn1SignedCertTemplate
), out
);
1041 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1042 signed_cert
.Data
, signed_cert
.Length
);
1046 PORT_FreeArena(poolp
, PR_TRUE
);
1047 CFReleaseSafe(pubkey_attrs
);
1053 SecIdentitySignCertificate(SecIdentityRef issuer
, CFDataRef serialno
,
1054 SecKeyRef publicKey
, CFTypeRef subject
, CFTypeRef extensions
)
1056 SecCertificateRef cert
= NULL
;
1057 SecKeyRef privateKey
= NULL
;
1059 PRArenaPool
*poolp
= PORT_NewArena(1024);
1060 CFDictionaryRef pubkey_attrs
= NULL
;
1064 NSS_Certificate cert_tmpl
;
1065 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
1068 unsigned char version
= 2;
1069 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
1070 cert_tmpl
.tbs
.version
.Data
= &version
;
1073 cert_tmpl
.tbs
.serialNumber
.Length
= CFDataGetLength(serialno
);
1074 cert_tmpl
.tbs
.serialNumber
.Data
= (uint8_t*)CFDataGetBytePtr(serialno
);
1076 /* subject/issuer */
1077 if (CFArrayGetTypeID() == CFGetTypeID(subject
))
1078 cert_tmpl
.tbs
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
1079 else if (CFDataGetTypeID() == CFGetTypeID(subject
)) {
1080 SecAsn1Item subject_item
= { CFDataGetLength(subject
), (uint8_t*)CFDataGetBytePtr(subject
) };
1081 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.subject
.rdns
, kSecAsn1NameTemplate
, &subject_item
), out
);
1085 SecCertificateRef issuer_cert
= NULL
;
1086 require_noerr(SecIdentityCopyCertificate(issuer
, &issuer_cert
), out
);
1087 CFDataRef issuer_name
= SecCertificateCopySubjectSequence(issuer_cert
);
1088 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_name
), (uint8_t*)CFDataGetBytePtr(issuer_name
) };
1089 require_noerr_action_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.issuer
.rdns
,
1090 kSecAsn1NameTemplate
, &issuer_item
), out
, CFReleaseNull(issuer_name
));
1091 CFReleaseNull(issuer_name
);
1093 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
1094 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
1095 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
1096 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
1100 if (CFDataGetTypeID() == CFGetTypeID(extensions
)) {
1101 SecAsn1Item requested_extensions
= { CFDataGetLength(extensions
), (uint8_t*)CFDataGetBytePtr(extensions
) };
1102 //NSS_CertExtension **requested_extensions_decoded;
1103 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.extensions
,
1104 kSecAsn1SequenceOfCertExtensionTemplate
, &requested_extensions
), out
);
1105 } else if (CFDictionaryGetTypeID() == CFGetTypeID(extensions
)) {
1106 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, extensions
);
1110 /* @@@ we only handle rsa keys */
1111 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
1112 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
1113 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
1114 /* public key data and algorithm */
1115 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
1116 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
1118 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
1119 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
1120 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
1122 /* signature algorithm */
1123 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
1124 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
1125 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
1126 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
1128 /* encode request info by itself to calculate signature */
1129 SecAsn1Item tbscert
= {};
1130 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
1132 /* calculate signature */
1133 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
1134 CCDigest(kCCDigestSHA1
, tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
1135 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
1136 size_t signature_length
= sizeof(signature
);
1138 require_noerr_quiet(SecIdentityCopyPrivateKey(issuer
, &privateKey
), out
);
1139 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
1140 tbscert_hash
, sizeof(tbscert_hash
), signature
, &signature_length
), out
);
1143 cert_tmpl
.signature
.Data
= signature
;
1144 cert_tmpl
.signature
.Length
= signature_length
* 8;
1147 SecAsn1Item signed_cert
= {};
1148 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1149 kSecAsn1SignedCertTemplate
), out
);
1150 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1151 signed_cert
.Data
, signed_cert
.Length
);
1154 CFReleaseSafe(privateKey
);
1156 PORT_FreeArena(poolp
, PR_TRUE
);
1157 CFReleaseSafe(pubkey_attrs
);
1163 SecGenerateCertificateRequestSubject(SecCertificateRef ca_certificate
, CFArrayRef subject
)
1165 CFMutableDataRef sequence
= NULL
;
1166 PRArenaPool
*poolp
= PORT_NewArena(1024);
1171 Going agains the spec here:
1173 3.2.3. GetCertInitial
1175 The messageData for this type consists of a DER-encoded
1176 IssuerAndSubject (Section 3.2.3.1). The issuer is set to the
1177 issuerName from the certification authority from which we are issued
1178 certificates. The Subject is set to the SubjectName we used when
1179 requesting the certificate.
1181 That clearly says use the issuer of the cert issuing certificate. Since
1182 it is combined with the subject of the to-be-issued certificate, that
1183 seems a mistake. If we take the subject of the issuer and the subject
1184 of the certificate we're interested in, we get the issuer and subject
1185 the certificate to be returned will have.
1188 CFDataRef issuer_sequence
= SecCertificateCopySubjectSequence(ca_certificate
);
1189 SecAsn1Item subject_item
= { 0 };
1190 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_sequence
), (uint8_t*)CFDataGetBytePtr(issuer_sequence
) };
1191 NSS_Name nss_subject
= { make_subject(poolp
, subject
) };
1192 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
, &nss_subject
, kSecAsn1NameTemplate
), out
);
1194 DERSize sequence_length
= DERLengthOfLength(subject_item
.Length
+ issuer_item
.Length
);
1195 DERSize seq_len_length
= subject_item
.Length
+ issuer_item
.Length
+ 1 /* SEQUENCE */ +
1197 sequence
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
1198 CFDataSetLength(sequence
, seq_len_length
);
1199 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1200 *sequence_ptr
++ = 0x30; //ASN1_CONSTR_SEQUENCE;
1201 require_noerr_quiet(DEREncodeLength(subject_item
.Length
+ issuer_item
.Length
, sequence_ptr
, &sequence_length
), out
);
1202 sequence_ptr
+= sequence_length
;
1203 memcpy(sequence_ptr
, issuer_item
.Data
, issuer_item
.Length
);
1204 memcpy(sequence_ptr
+ issuer_item
.Length
, subject_item
.Data
, subject_item
.Length
);
1207 CFReleaseSafe(issuer_sequence
);
1209 PORT_FreeArena(poolp
, PR_TRUE
);