2 * Copyright (c) 2008-2009,2012-2017 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>
59 #include <Security/SecECKey.h>
62 #include <utilities/SecCFWrappers.h>
64 #include <AssertMacros.h>
66 #include "SecCertificateRequest.h"
68 CFTypeRef kSecOidCommonName
= CFSTR("CN");
69 CFTypeRef kSecOidCountryName
= CFSTR("C");
70 CFTypeRef kSecOidStateProvinceName
= CFSTR("ST");
71 CFTypeRef kSecOidLocalityName
= CFSTR("L");
72 CFTypeRef kSecOidOrganization
= CFSTR("O");
73 CFTypeRef kSecOidOrganizationalUnit
= CFSTR("OU");
74 //CFTypeRef kSecOidEmailAddress = CFSTR("1.2.840.113549.1.9.1");
75 // keep natural order: C > ST > L > O > OU > CN > Email
77 const unsigned char SecASN1PrintableString
= SEC_ASN1_PRINTABLE_STRING
;
78 const unsigned char SecASN1UTF8String
= SEC_ASN1_UTF8_STRING
;
80 static uint8_t * mod128_oid_encoding_ptr(uint8_t *ptr
, uint32_t src
, bool final
)
83 ptr
= mod128_oid_encoding_ptr(ptr
, src
/ 128, false);
85 unsigned char octet
= src
% 128;
93 static uint8_t * oid_der_data(PRArenaPool
*poolp
, CFStringRef oid_string
, size_t *oid_data_len
)
95 CFArrayRef oid
= NULL
;
96 /* estimate encoded length from base 10 (4 bits) to base 128 (7 bits) */
97 require(((size_t)CFStringGetLength(oid_string
) < (SIZE_MAX
/4)), out
); // Guard against integer overflow on size_t
98 size_t tmp_oid_length
= ((((size_t)CFStringGetLength(oid_string
)) * 4) / 7) + 1;
99 uint8_t *tmp_oid_data
= PORT_ArenaAlloc(poolp
, tmp_oid_length
);
100 uint8_t *tmp_oid_data_ptr
= tmp_oid_data
;
101 require(tmp_oid_data
, out
); // Allocation failure
102 oid
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
,
103 oid_string
, CFSTR("."));
104 CFIndex i
= 0, count
= CFArrayGetCount(oid
);
105 SInt32 first_digit
= 0, digit
;
106 for (i
= 0; i
< count
; i
++) {
107 CFStringRef oid_octet
= CFArrayGetValueAtIndex(oid
, i
);
108 SInt32 oid_octet_int_value
= CFStringGetIntValue(oid_octet
);
109 require(abs(oid_octet_int_value
) != INT32_MAX
, out
);
111 first_digit
= oid_octet_int_value
;
114 digit
= 40 * first_digit
+ oid_octet_int_value
;
116 digit
= oid_octet_int_value
;
117 tmp_oid_data_ptr
= mod128_oid_encoding_ptr(tmp_oid_data_ptr
, digit
, true);
122 *oid_data_len
= tmp_oid_data_ptr
- tmp_oid_data
;
131 Get challenge password conversion and apply this:
133 ASCII ? => PrintableString subset: [A-Za-z0-9 '()+,-./:=?] ?
135 PrintableString > IA5String > UTF8String
137 Consider using IA5String for email address
140 static inline bool printable_string(CFStringRef string
)
144 CFCharacterSetRef printable_charset
=
145 CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
,
146 CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
147 "abcdefghijklmnopqrstuvwxyz"
148 "0123456789 '()+,-./:=?"));
149 CFCharacterSetRef not_printable_charset
=
150 CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, printable_charset
);
152 if (CFStringFindCharacterFromSet(string
, not_printable_charset
,
153 CFRangeMake(0, CFStringGetLength(string
)), 0, &found
))
156 CFReleaseSafe(printable_charset
);
157 CFReleaseSafe(not_printable_charset
);
162 static bool make_nss_atv(PRArenaPool
*poolp
,
163 const void * oid
, const void * value
, const unsigned char type_in
, NSS_ATV
*nss_atv
)
167 unsigned char type
= type_in
;
168 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
169 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
170 kCFStringEncodingUTF8
);
171 buffer
= PORT_ArenaAlloc(poolp
, length
);
172 /* TODO: Switch to using CFStringGetBytes,since this code will do the wrong thing for embedded 0's */
173 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingASCII
)) {
174 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
176 if (type
&& type
!= SecASN1UTF8String
)
178 type
= SecASN1UTF8String
;
181 if (!type
|| type
== SecASN1PrintableString
) {
182 if (!printable_string(value
))
183 type
= SEC_ASN1_IA5_STRING
;
185 type
= SEC_ASN1_PRINTABLE_STRING
;
188 length
= strlen(buffer
);
190 else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
191 /* will remain valid for the duration of the operation, still maybe copy into pool */
192 length
= CFDataGetLength(value
);
193 buffer
= (char *)CFDataGetBytePtr(value
);
195 size_t oid_length
= 0;
196 uint8_t *oid_data
= NULL
;
197 if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
198 if (CFEqual(kSecOidCommonName
, oid
)) {
199 oid_length
= oidCommonName
.length
; oid_data
= oidCommonName
.data
;
200 } else if (CFEqual(kSecOidCountryName
, oid
)) {
201 oid_length
= oidCountryName
.length
; oid_data
= oidCountryName
.data
;
202 } else if (CFEqual(kSecOidStateProvinceName
, oid
)) {
203 oid_length
= oidStateOrProvinceName
.length
; oid_data
= oidStateOrProvinceName
.data
;
204 } else if (CFEqual(kSecOidLocalityName
, oid
)) {
205 oid_length
= oidLocalityName
.length
; oid_data
= oidLocalityName
.data
;
206 } else if (CFEqual(kSecOidOrganization
, oid
)) {
207 oid_length
= oidOrganizationName
.length
; oid_data
= oidOrganizationName
.data
;
208 } else if (CFEqual(kSecOidOrganizationalUnit
, oid
)) {
209 oid_length
= oidOrganizationalUnitName
.length
; oid_data
= oidOrganizationalUnitName
.data
;
211 oid_data
= oid_der_data(poolp
, oid
, &oid_length
);
212 require(oid_data
, out
);
214 } else if (CFGetTypeID(oid
) == CFDataGetTypeID()) {
215 /* will remain valid for the duration of the operation, still maybe copy into pool */
216 oid_length
= CFDataGetLength(oid
);
217 oid_data
= (uint8_t *)CFDataGetBytePtr(oid
);
219 NSS_ATV stage_nss_atv
= { { oid_length
, oid_data
},
220 { { length
, (uint8_t*)buffer
}, type
} };
221 *nss_atv
= stage_nss_atv
;
227 static NSS_RDN
**make_subject(PRArenaPool
*poolp
, CFArrayRef subject
)
231 CFIndex rdn_ix
, rdn_count
= CFArrayGetCount(subject
);
232 NSS_RDN
**rdnps
= PORT_ArenaZNewArray(poolp
, NSS_RDN
*, rdn_count
+ 1);
233 NSS_RDN
*rdns
= PORT_ArenaZNewArray(poolp
, NSS_RDN
, rdn_count
);
234 for (rdn_ix
= 0; rdn_ix
< rdn_count
; rdn_ix
++) {
235 rdnps
[rdn_ix
] = &rdns
[rdn_ix
];
236 CFArrayRef rdn
= CFArrayGetValueAtIndex(subject
, rdn_ix
);
237 CFIndex atv_ix
, atv_count
= CFArrayGetCount(rdn
);
238 rdns
[rdn_ix
].atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
*, atv_count
+ 1);
239 NSS_ATV
*atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
, atv_count
);
240 for (atv_ix
= 0; atv_ix
< atv_count
; atv_ix
++) {
241 rdns
[rdn_ix
].atvs
[atv_ix
] = &atvs
[atv_ix
];
242 CFArrayRef atv
= CFArrayGetValueAtIndex(rdn
, atv_ix
);
243 if ((CFArrayGetCount(atv
) != 2)
244 || !make_nss_atv(poolp
, CFArrayGetValueAtIndex(atv
, 0),
245 CFArrayGetValueAtIndex(atv
, 1), 0, &atvs
[atv_ix
]))
252 struct make_general_names_context
{
259 static void make_general_names(const void *key
, const void *value
, void *context
)
261 struct make_general_names_context
*gn
= (struct make_general_names_context
*)context
;
263 CFArrayRef gn_values
= NULL
;
264 CFStringRef gn_value
= NULL
;
265 CFIndex entry_ix
, entry_count
= 0;
266 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
267 gn_values
= (CFArrayRef
)value
;
268 entry_count
= CFArrayGetCount(value
);
269 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
270 gn_value
= (CFStringRef
)value
;
274 require(entry_count
> 0, out
);
277 require(CFGetTypeID(key
) == CFStringGetTypeID(), out
);
279 if (!gn
->names
|| (gn
->count
== gn
->capacity
)) {
280 uint32_t capacity
= gn
->capacity
;
286 void * new_array
= PORT_ArenaZNewArray(gn
->poolp
, SecAsn1Item
, capacity
);
288 memcpy(new_array
, gn
->names
, gn
->capacity
);
289 gn
->names
= new_array
;
290 gn
->capacity
= capacity
;
293 NSS_GeneralName general_name_item
= { { }, -1 };
294 if (kCFCompareEqualTo
== CFStringCompare(CFSTR("dNSName"), key
, kCFCompareCaseInsensitive
))
295 general_name_item
.tag
= NGT_DNSName
;
296 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("rfc822Name"), key
, kCFCompareCaseInsensitive
))
297 general_name_item
.tag
= NGT_RFC822Name
;
298 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("uniformResourceIdentifier"), key
, kCFCompareCaseInsensitive
))
299 general_name_item
.tag
= NGT_URI
;
300 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("ntPrincipalName"), key
, kCFCompareCaseInsensitive
))
303 NT Principal in SubjectAltName is defined in the context of Smartcards:
305 http://www.oid-info.com/get/1.3.6.1.4.1.311.20.2.3
306 http://support.microsoft.com/default.aspx?scid=kb;en-us;281245
308 Subject Alternative Name = Other Name: Principal Name= (UPN). For example:
310 The UPN OtherName OID is : "1.3.6.1.4.1.311.20.2.3"
311 The UPN OtherName value: Must be ASN1-encoded UTF8 string
312 Subject = Distinguished name of user. This field is a mandatory extension, but the population of this field is optional.
315 /* OtherName ::= SEQUENCE {
316 type-id OBJECT IDENTIFIER,
317 value [0] EXPLICIT ANY DEFINED BY type-id
319 uint8_t nt_principal_oid
[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03 };
323 } nt_principal_other_name
;
324 nt_principal_other_name name
= {};
326 const SecAsn1Template my_other_name_template
[] = {
328 0, NULL
, sizeof(nt_principal_other_name
) },
329 { SEC_ASN1_OBJECT_ID
,
330 offsetof(nt_principal_other_name
,typeId
), },
331 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| 0, offsetof(nt_principal_other_name
,value
), kSecAsn1UTF8StringTemplate
, },
334 const SecAsn1Template my_other_name_template_cons
[] = {
335 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| NGT_OtherName
,
336 0, my_other_name_template
, sizeof(nt_principal_other_name
) }
342 require(gn_value
, out
);
343 require(CFGetTypeID(gn_value
) == CFStringGetTypeID(), out
);
344 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
345 kCFStringEncodingUTF8
);
346 buffer
= PORT_ArenaAlloc(gn
->poolp
, length
);
347 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
350 name
.typeId
.Length
= sizeof(nt_principal_oid
);
351 name
.typeId
.Data
= nt_principal_oid
;
352 name
.value
.Length
= strlen(buffer
);
353 name
.value
.Data
= (uint8_t*)buffer
;
354 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &name
, my_other_name_template_cons
);
357 /* We already encoded the value for the general name */
364 for (entry_ix
= 0; entry_ix
< entry_count
; entry_ix
++) {
365 CFTypeRef entry_value
= CFArrayGetValueAtIndex(gn_values
, entry_ix
);
366 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
)entry_value
),
367 kCFStringEncodingUTF8
); /* we only allow ASCII => only expect IA5Strings */
368 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
369 require(CFStringGetCString((CFStringRef
)entry_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
370 general_name_item
.item
.Data
= (uint8_t*)buffer
;
371 general_name_item
.item
.Length
= strlen(buffer
);
372 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
375 } else if (gn_value
) {
376 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(gn_value
),
377 kCFStringEncodingUTF8
);
378 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
379 require(CFStringGetCString(gn_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
380 general_name_item
.item
.Data
= (uint8_t*)buffer
;
381 general_name_item
.item
.Length
= strlen(buffer
);
382 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
389 static SecAsn1Item
make_subjectAltName_extension(PRArenaPool
*poolp
, CFDictionaryRef subjectAltNames
)
391 SecAsn1Item subjectAltExt
= {};
393 struct make_general_names_context context
= { poolp
, NULL
, 0 };
394 CFDictionaryApplyFunction(subjectAltNames
, make_general_names
, &context
);
396 // all general names in a sequence:
398 SecAsn1Item
**general_names
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, context
.count
+ 1);
399 for (ix
= 0; ix
< context
.count
; ix
++)
400 general_names
[ix
] = &context
.names
[ix
];
401 NSS_GeneralNames gnames
= { general_names
};
402 SEC_ASN1EncodeItem(poolp
, &subjectAltExt
, &gnames
, kSecAsn1GeneralNamesTemplate
);
404 return subjectAltExt
;
407 CFTypeRef kSecCSRChallengePassword
= CFSTR("csrChallengePassword");
408 CFTypeRef kSecSubjectAltName
= CFSTR("subjectAltName");
409 CFTypeRef kSecCertificateKeyUsage
= CFSTR("keyUsage");
410 CFTypeRef kSecCSRBasicContraintsPathLen
= CFSTR("basicConstraints");
411 CFTypeRef kSecCertificateExtensions
= CFSTR("certificateExtensions");
412 CFTypeRef kSecCertificateExtensionsEncoded
= CFSTR("certificateExtensionsEncoded");
414 static const uint8_t pkcs9ExtensionsRequested
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 14 };
415 static const uint8_t pkcs9ChallengePassword
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 7 };
417 static const uint8_t encoded_asn1_true
= 0xFF;
418 static const SecAsn1Item asn1_true
=
419 { sizeof(encoded_asn1_true
), (uint8_t*)&encoded_asn1_true
};
421 struct add_custom_extension_args
{
423 NSS_CertExtension
*csr_extension
;
424 uint32_t num_extensions
;
425 uint32_t max_extensions
;
429 static void add_custom_extension(const void *key
, const void *value
, void *context
)
431 struct add_custom_extension_args
*args
= (struct add_custom_extension_args
*)context
;
434 require(args
->num_extensions
< args
->max_extensions
, out
);
436 uint8_t * der_data
= oid_der_data(args
->poolp
, key
, &der_data_len
);
437 SecAsn1Item encoded_value
= {};
439 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
440 if (!args
->encodeData
) {
443 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
), kCFStringEncodingUTF8
);
444 char *buffer
= (char *)PORT_ArenaZNewArray(args
->poolp
, uint8_t, buffer_size
);
445 if (!CFStringGetCString(value
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
448 SecAsn1Item buffer_item
= { strlen(buffer
), (uint8_t*)buffer
};
449 SEC_ASN1EncodeItem(args
->poolp
, &encoded_value
, &buffer_item
, kSecAsn1UTF8StringTemplate
);
450 } else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
451 if (args
->encodeData
) {
452 SecAsn1Item data_item
= { CFDataGetLength(value
), (uint8_t*)CFDataGetBytePtr(value
) };
453 SEC_ASN1EncodeItem(args
->poolp
, &encoded_value
, &data_item
, kSecAsn1OctetStringTemplate
);
456 encoded_value
.Length
= CFDataGetLength(value
);
457 encoded_value
.Data
= (uint8_t*)CFDataGetBytePtr(value
);
463 if (der_data
&& encoded_value
.Length
) {
464 args
->csr_extension
[args
->num_extensions
].value
= encoded_value
;
465 args
->csr_extension
[args
->num_extensions
].extnId
.Length
= der_data_len
;
466 args
->csr_extension
[args
->num_extensions
].extnId
.Data
= der_data
;
467 args
->num_extensions
++;
475 extensions_from_parameters(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
477 uint32_t num_extensions
= 0, max_extensions
= 10;
478 NSS_CertExtension
**csr_extensions
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
*, max_extensions
+ 1); /* NULL terminated array */
479 NSS_CertExtension
*csr_extension
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
, max_extensions
);
481 CFNumberRef basic_contraints_num
= CFDictionaryGetValue(parameters
, kSecCSRBasicContraintsPathLen
);
482 if (basic_contraints_num
) {
483 NSS_BasicConstraints basic_contraints
= { asn1_true
, {} };
486 int basic_contraints_path_len
= 0;
487 require(CFNumberGetValue(basic_contraints_num
, kCFNumberIntType
, &basic_contraints_path_len
), out
);
488 if (basic_contraints_path_len
>= 0 && basic_contraints_path_len
< 256) {
489 path_len
= (uint8_t)basic_contraints_path_len
;
490 basic_contraints
.pathLenConstraint
.Length
= sizeof(path_len
);
491 basic_contraints
.pathLenConstraint
.Data
= &path_len
;
494 csr_extension
[num_extensions
].extnId
.Data
= oidBasicConstraints
.data
;
495 csr_extension
[num_extensions
].extnId
.Length
= oidBasicConstraints
.length
;
496 csr_extension
[num_extensions
].critical
= asn1_true
;
498 SEC_ASN1EncodeItem(poolp
, &csr_extension
[num_extensions
].value
, &basic_contraints
,
499 kSecAsn1BasicConstraintsTemplate
);
500 require(num_extensions
++ < max_extensions
, out
);
503 CFDictionaryRef subject_alternate_names
= CFDictionaryGetValue(parameters
, kSecSubjectAltName
);
504 if (subject_alternate_names
) {
505 require(CFGetTypeID(subject_alternate_names
) == CFDictionaryGetTypeID(), out
);
506 csr_extension
[num_extensions
].value
= make_subjectAltName_extension(poolp
, subject_alternate_names
);
507 /* set up subjectAltName cert request value */
508 csr_extension
[num_extensions
].extnId
.Length
= oidSubjectAltName
.length
;
509 csr_extension
[num_extensions
].extnId
.Data
= oidSubjectAltName
.data
;
510 require(num_extensions
++ < max_extensions
, out
);
513 CFNumberRef key_usage_requested
= CFDictionaryGetValue(parameters
, kSecCertificateKeyUsage
);
514 SecAsn1Item key_usage_asn1_value
= { 0 };
515 if (key_usage_requested
) {
517 require(CFNumberGetValue(key_usage_requested
, kCFNumberIntType
, &key_usage_value
), out
);
518 if (key_usage_value
> 0) {
519 uint32_t key_usage_value_be
= 0, key_usage_mask
= 1<<31;
520 uint32_t key_usage_value_max_bitlen
= 9, key_usage_value_bitlen
= 0;
521 while(key_usage_value_max_bitlen
) {
522 if (key_usage_value
& 1) {
523 key_usage_value_be
|= key_usage_mask
;
524 key_usage_value_bitlen
= 10 - key_usage_value_max_bitlen
;
526 key_usage_value
>>= 1;
527 key_usage_value_max_bitlen
--;
528 key_usage_mask
>>= 1;
531 SecAsn1Item key_usage_input
= { key_usage_value_bitlen
,
532 ((uint8_t*)&key_usage_value_be
) + 3 - (key_usage_value_bitlen
>> 3) };
533 SEC_ASN1EncodeItem(poolp
, &key_usage_asn1_value
, &key_usage_input
, kSecAsn1BitStringTemplate
);
535 csr_extension
[num_extensions
].extnId
.Data
= oidKeyUsage
.data
;
536 csr_extension
[num_extensions
].extnId
.Length
= oidKeyUsage
.length
;
537 csr_extension
[num_extensions
].critical
= asn1_true
;
538 csr_extension
[num_extensions
].value
= key_usage_asn1_value
;
539 require(num_extensions
++ < max_extensions
, out
);
543 CFDictionaryRef custom_extension_requested
= CFDictionaryGetValue(parameters
, kSecCertificateExtensions
);
544 if (custom_extension_requested
) {
545 require(CFGetTypeID(custom_extension_requested
) == CFDictionaryGetTypeID(), out
);
546 struct add_custom_extension_args args
= {
553 CFDictionaryApplyFunction(custom_extension_requested
, add_custom_extension
, &args
);
554 num_extensions
= args
.num_extensions
;
557 CFDictionaryRef custom_encoded_extension_requested
= CFDictionaryGetValue(parameters
, kSecCertificateExtensionsEncoded
);
558 if (custom_encoded_extension_requested
) {
559 require(CFGetTypeID(custom_encoded_extension_requested
) == CFDictionaryGetTypeID(), out
);
560 struct add_custom_extension_args args
= {
567 CFDictionaryApplyFunction(custom_encoded_extension_requested
, add_custom_extension
, &args
);
568 num_extensions
= args
.num_extensions
;
571 /* extensions requested (subjectAltName, keyUsage) sequence of extension sequences */
573 for (ix
= 0; ix
< num_extensions
; ix
++)
574 csr_extensions
[ix
] = csr_extension
[ix
].extnId
.Length
? &csr_extension
[ix
] : NULL
;
577 return csr_extensions
;
583 NSS_Attribute
**nss_attributes_from_parameters_dict(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
585 /* A challenge-password attribute must have a single attribute value.
587 ChallengePassword attribute values generated in accordance with this
588 version of this document SHOULD use the PrintableString encoding
589 whenever possible. If internationalization issues make this
590 impossible, the UTF8String alternative SHOULD be used. PKCS #9-
591 attribute processing systems MUST be able to recognize and process
592 all string types in DirectoryString values.
594 Upperbound of 255 defined for all PKCS#9 attributes.
596 pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840)
597 rsadsi(113549) pkcs(1) 9}
598 pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7}
603 uint32_t num_attrs
= 0;
605 CFStringRef challenge
= CFDictionaryGetValue(parameters
, kSecCSRChallengePassword
);
606 NSS_Attribute challenge_password_attr
= {};
608 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(challenge
),
609 kCFStringEncodingUTF8
); /* we only allow UTF8 or ASCII */
610 char *buffer
= (char *)PORT_ArenaZNewArray(poolp
, uint8_t, buffer_size
);
612 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingASCII
)) {
613 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
617 if (!printable_string(challenge
))
620 SecAsn1Item
*challenge_password_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
621 SecAsn1Item challenge_password_raw
= { strlen(buffer
), (uint8_t*)buffer
};
622 SEC_ASN1EncodeItem(poolp
, challenge_password_value
, &challenge_password_raw
,
623 utf8
? kSecAsn1UTF8StringTemplate
: kSecAsn1PrintableStringTemplate
);
624 SecAsn1Item
**challenge_password_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
625 challenge_password_values
[0] = challenge_password_value
;
626 challenge_password_attr
.attrType
.Length
= sizeof(pkcs9ChallengePassword
);
627 challenge_password_attr
.attrType
.Data
= (uint8_t*)&pkcs9ChallengePassword
;
628 challenge_password_attr
.attrValue
= challenge_password_values
;
632 NSS_CertExtension
**extensions
= extensions_from_parameters(poolp
, parameters
);
633 NSS_Attribute extensions_requested_attr
= {};
635 SecAsn1Item
*extensions_requested_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
636 SEC_ASN1EncodeItem(poolp
, extensions_requested_value
, &extensions
, kSecAsn1SequenceOfCertExtensionTemplate
);
637 SecAsn1Item
**extensions_requested_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
638 extensions_requested_values
[0] = extensions_requested_value
;
639 extensions_requested_values
[1] = NULL
;
640 extensions_requested_attr
.attrType
.Length
= sizeof(pkcs9ExtensionsRequested
);
641 extensions_requested_attr
.attrType
.Data
= (uint8_t*)pkcs9ExtensionsRequested
;
642 extensions_requested_attr
.attrValue
= extensions_requested_values
;
646 NSS_Attribute
**attributes_ptr
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
*, num_attrs
+ 1);
647 NSS_Attribute
*attributes
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
, num_attrs
);
648 if (challenge_password_attr
.attrType
.Length
) {
650 attributes
[num_attrs
] = challenge_password_attr
;
651 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
653 if (extensions_requested_attr
.attrType
.Length
) {
655 attributes
[num_attrs
] = extensions_requested_attr
;
656 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
658 return attributes_ptr
;
665 static const uint8_t encoded_null
[2] = { SEC_ASN1_NULL
, 0 };
666 static const SecAsn1Item asn1_null
= { sizeof(encoded_null
), (uint8_t*)encoded_null
};
668 CFDataRef
SecGenerateCertificateRequestWithParameters(SecRDN
*subject
,
669 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
671 if (subject
== NULL
|| *subject
== NULL
) {
675 CFDataRef csr
= NULL
;
676 CFDataRef publicKeyData
= NULL
;
677 uint8_t *signature
= NULL
, *spki_params
= NULL
;
678 PRArenaPool
*poolp
= PORT_NewArena(1024);
683 NSSCertRequest certReq
;
684 memset(&certReq
, 0, sizeof(certReq
));
687 unsigned char version
= 0;
688 certReq
.reqInfo
.version
.Length
= sizeof(version
);
689 certReq
.reqInfo
.version
.Data
= &version
;
692 unsigned atv_num
= 0, num
= 0;
695 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
696 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++)
698 atv_num
++; /* one more */
701 const unsigned min1atv_num
= atv_num
> 0 ? atv_num
: 1;
702 const unsigned min1num
= num
> 0 ? num
: 1;
703 NSS_ATV atvs
[min1atv_num
];
704 NSS_ATV
*atvps
[min1atv_num
];
705 NSS_RDN rdns
[min1num
];
706 NSS_RDN
*rdnps
[num
+1];
708 unsigned rdn_num
= 0;
709 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
710 rdns
[rdn_num
].atvs
= &atvps
[atv_num
];
711 rdnps
[rdn_num
] = &rdns
[rdn_num
];
713 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++) {
714 if (!make_nss_atv(poolp
, one_atv
->oid
, one_atv
->value
,
715 one_atv
->type
, &atvs
[atv_num
]))
717 atvps
[atv_num
] = &atvs
[atv_num
];
720 atvps
[atv_num
++] = NULL
;
722 rdnps
[rdn_num
] = NULL
;
723 certReq
.reqInfo
.subject
.rdns
= rdnps
;
725 /* public key info */
726 if (SecKeyGetAlgorithmId(publicKey
) == kSecRSAAlgorithmID
) {
727 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
728 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
729 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
730 } else if (SecKeyGetAlgorithmId(publicKey
) == kSecECDSAAlgorithmID
) {
731 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidEcPubKey
.length
;
732 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidEcPubKey
.data
;
733 size_t parameters_size
= 0;
734 SecECNamedCurve namedCurve
= SecECKeyGetNamedCurve(publicKey
);
735 switch (namedCurve
) {
736 case kSecECCurveSecp256r1
:
737 parameters_size
= oidEcPrime256v1
.length
+ 2;
738 spki_params
= malloc(parameters_size
);
739 memcpy(spki_params
+ 2, oidEcPrime256v1
.data
, oidEcPrime256v1
.length
);
741 case kSecECCurveSecp384r1
:
742 parameters_size
= oidAnsip384r1
.length
+ 2;
743 spki_params
= malloc(parameters_size
);
744 memcpy(spki_params
+ 2, oidAnsip384r1
.data
, oidAnsip384r1
.length
);
746 case kSecECCurveSecp521r1
:
747 parameters_size
= oidAnsip521r1
.length
+ 2;
748 spki_params
= malloc(parameters_size
);
749 memcpy(spki_params
+ 2, oidAnsip521r1
.data
, oidAnsip521r1
.length
);
754 spki_params
[0] = 0x06;
755 spki_params
[1] = (uint8_t)(parameters_size
- 2);
756 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
.Length
= parameters_size
;
757 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
.Data
= spki_params
;
760 publicKeyData
= SecKeyCopyExternalRepresentation(publicKey
, NULL
);
761 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(publicKeyData
);
762 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(publicKeyData
);
764 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
765 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
767 /* encode request info by itself to calculate signature */
768 SecAsn1Item reqinfo
= {};
769 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
771 /* Use SHA-1 for RSA for backwards compatbility. */
772 if (SecKeyGetAlgorithmId(privateKey
) == kSecRSAAlgorithmID
) {
773 /* calculate signature */
774 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
775 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
776 CFDataRef digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, reqinfo_hash
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
777 CFDataRef sigData
= SecKeyCreateSignature(privateKey
, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
,
779 CFReleaseNull(digest
);
780 require_quiet(sigData
, out
);
781 size_t signature_length
= (size_t)CFDataGetLength(sigData
);
782 signature
= malloc(signature_length
);
783 memcpy(signature
, CFDataGetBytePtr(sigData
), CFDataGetLength(sigData
));
784 CFReleaseNull(sigData
);
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 } else if (SecKeyGetAlgorithmId(privateKey
) == kSecECDSAAlgorithmID
) {
793 /* calculate signature */
794 uint8_t reqinfo_hash
[CC_SHA256_DIGEST_LENGTH
];
795 CCDigest(kCCDigestSHA256
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
796 CFDataRef digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, reqinfo_hash
, CC_SHA256_DIGEST_LENGTH
, kCFAllocatorNull
);
797 CFDataRef sigData
= SecKeyCreateSignature(privateKey
, kSecKeyAlgorithmECDSASignatureDigestX962SHA256
,
799 CFReleaseNull(digest
);
800 require_quiet(sigData
, out
);
801 size_t signature_length
= (size_t)CFDataGetLength(sigData
);
802 signature
= malloc(signature_length
);
803 memcpy(signature
, CFDataGetBytePtr(sigData
), CFDataGetLength(sigData
));
804 CFReleaseNull(sigData
);
806 /* signature and info */
807 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha256Ecdsa
.length
;
808 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha256Ecdsa
.data
;
809 certReq
.signatureAlgorithm
.parameters
.Data
= NULL
;
810 certReq
.signatureAlgorithm
.parameters
.Length
= 0;
811 certReq
.signature
.Data
= signature
;
812 certReq
.signature
.Length
= signature_length
* 8;
816 SecAsn1Item cert_request
= {};
817 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
818 kSecAsn1CertRequestTemplate
), out
);
819 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
823 PORT_FreeArena(poolp
, PR_TRUE
);
824 if (signature
) { free(signature
); }
825 if (spki_params
) { free(spki_params
); }
826 CFReleaseSafe(publicKeyData
);
830 CFDataRef
SecGenerateCertificateRequest(CFArrayRef subject
,
831 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
833 CFDataRef csr
= NULL
;
834 PRArenaPool
*poolp
= PORT_NewArena(1024);
835 CFDataRef publicKeyData
= NULL
;
836 uint8_t *signature
= NULL
;
841 NSSCertRequest certReq
;
842 memset(&certReq
, 0, sizeof(certReq
));
845 unsigned char version
= 0;
846 certReq
.reqInfo
.version
.Length
= sizeof(version
);
847 certReq
.reqInfo
.version
.Data
= &version
;
850 certReq
.reqInfo
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
852 /* public key info */
853 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
854 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
855 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
857 publicKeyData
= SecKeyCopyExternalRepresentation(publicKey
, NULL
);
858 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(publicKeyData
);
859 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(publicKeyData
);
861 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
862 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
864 /* encode request info by itself to calculate signature */
865 SecAsn1Item reqinfo
= {};
866 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
868 /* calculate signature */
869 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
870 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, reqinfo
.Length
, reqinfo_hash
);
871 CFDataRef digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, reqinfo_hash
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
872 CFDataRef sigData
= SecKeyCreateSignature(privateKey
, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
,
874 CFReleaseNull(digest
);
875 require_quiet(sigData
, out
);
876 size_t signature_length
= (size_t)CFDataGetLength(sigData
);
877 signature
= malloc(signature_length
);
878 memcpy(signature
, CFDataGetBytePtr(sigData
), CFDataGetLength(sigData
));
879 CFReleaseNull(sigData
);
881 /* signature and info */
882 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha1Rsa
.length
;
883 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha1Rsa
.data
;
884 certReq
.signatureAlgorithm
.parameters
= asn1_null
;
885 certReq
.signature
.Data
= signature
;
886 certReq
.signature
.Length
= signature_length
* 8;
889 SecAsn1Item cert_request
= {};
890 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
891 kSecAsn1CertRequestTemplate
), out
);
892 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
896 PORT_FreeArena(poolp
, PR_TRUE
);
897 CFReleaseSafe(publicKeyData
);
898 if (signature
) { free(signature
); }
902 bool SecVerifyCertificateRequest(CFDataRef csr
, SecKeyRef
*publicKey
,
903 CFStringRef
*challenge
, CFDataRef
*subject
, CFDataRef
*extensions
)
905 PRArenaPool
*poolp
= PORT_NewArena(1024);
906 SecKeyRef candidatePublicKey
= NULL
;
907 CFMutableDictionaryRef keyAttrs
= NULL
;
908 CFDataRef keyData
= NULL
, hash
= NULL
, signature
= NULL
;
910 NSSCertRequest certReq
;
911 memset(&certReq
, 0, sizeof(certReq
));
912 SecAsn1Item csr_item
= { CFDataGetLength(csr
), (uint8_t*)CFDataGetBytePtr(csr
) };
913 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &certReq
, kSecAsn1CertRequestTemplate
,
916 /* signature and info */
917 require(certReq
.signatureAlgorithm
.algorithm
.Length
== oidSha1Rsa
.length
||
918 certReq
.signatureAlgorithm
.algorithm
.Length
== oidSha256Ecdsa
.length
, out
);
919 require(0 == memcmp(oidSha1Rsa
.data
, certReq
.signatureAlgorithm
.algorithm
.Data
,
920 oidSha1Rsa
.length
) ||
921 0 == memcmp(oidSha256Ecdsa
.data
, certReq
.signatureAlgorithm
.algorithm
.Data
,
922 oidSha256Ecdsa
.length
), out
);
923 require(certReq
.signatureAlgorithm
.parameters
.Length
== asn1_null
.Length
||
924 certReq
.signatureAlgorithm
.parameters
.Length
== 0, out
);
925 require(certReq
.signatureAlgorithm
.parameters
.Length
== 0 ||
926 0 == memcmp(asn1_null
.Data
, certReq
.signatureAlgorithm
.parameters
.Data
,
927 asn1_null
.Length
), out
);
929 /* encode request info by itself to calculate signature */
930 SecAsn1Item reqinfo
= {};
931 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
933 /* calculate signature */
934 uint8_t reqinfo_hash
[CC_SHA256_DIGEST_LENGTH
];
935 CFIndex hash_size
= 0;
936 if (0 == memcmp(oidSha1Rsa
.data
, certReq
.signatureAlgorithm
.algorithm
.Data
,
937 oidSha1Rsa
.length
)) {
938 require(reqinfo
.Length
<=UINT32_MAX
, out
);
939 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
940 hash_size
= CC_SHA1_DIGEST_LENGTH
;
942 require(reqinfo
.Length
<=UINT32_MAX
, out
);
943 CCDigest(kCCDigestSHA256
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
944 hash_size
= CC_SHA256_DIGEST_LENGTH
;
947 /* @@@ check for version 0 */
948 SecKeyAlgorithm alg
= NULL
;
949 if (certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
== oidRsa
.length
&&
950 0 == memcmp(oidRsa
.data
, certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
, oidRsa
.length
)) {
951 require(candidatePublicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
952 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
,
953 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
/ 8,
954 kSecKeyEncodingPkcs1
), out
);
955 alg
= kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
;
956 } else if (certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
== oidEcPubKey
.length
&&
957 0 == memcmp(oidEcPubKey
.data
, certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
, oidEcPubKey
.length
)) {
958 keyData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
959 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
,
960 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
/ 8,
962 keyAttrs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
963 &kCFTypeDictionaryValueCallBacks
);
964 CFDictionaryAddValue(keyAttrs
, kSecAttrKeyType
, kSecAttrKeyTypeECSECPrimeRandom
);
965 CFDictionaryAddValue(keyAttrs
, kSecAttrKeyClass
, kSecAttrKeyClassPublic
);
966 require(candidatePublicKey
= SecKeyCreateWithData(keyData
, keyAttrs
, NULL
),
968 alg
= kSecKeyAlgorithmECDSASignatureDigestX962SHA256
;
973 hash
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, reqinfo_hash
, hash_size
, kCFAllocatorNull
);
974 signature
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, certReq
.signature
.Data
, certReq
.signature
.Length
/ 8, kCFAllocatorNull
);
975 require_quiet(SecKeyVerifySignature(candidatePublicKey
, alg
, hash
, signature
, NULL
), out
);
977 SecAsn1Item subject_item
= { 0 }, extensions_item
= { 0 }, challenge_item
= { 0 };
978 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
,
979 &certReq
.reqInfo
.subject
, kSecAsn1NameTemplate
), out
);
981 if (*certReq
.reqInfo
.attributes
) {
983 for (ix
= 0; certReq
.reqInfo
.attributes
[ix
]; ix
++) {
984 NSS_Attribute
*attr
= certReq
.reqInfo
.attributes
[ix
];
985 if ( (sizeof(pkcs9ChallengePassword
) == attr
->attrType
.Length
) &&
986 !memcmp(pkcs9ChallengePassword
, attr
->attrType
.Data
, sizeof(pkcs9ChallengePassword
)))
987 challenge_item
= *attr
->attrValue
[0];
988 else if ( (sizeof(pkcs9ExtensionsRequested
) == attr
->attrType
.Length
) &&
989 !memcmp(pkcs9ExtensionsRequested
, attr
->attrType
.Data
, sizeof(pkcs9ExtensionsRequested
)))
990 extensions_item
= *attr
->attrValue
[0];
994 if (subject
&& subject_item
.Length
)
995 *subject
= CFDataCreate(kCFAllocatorDefault
, subject_item
.Data
, subject_item
.Length
);
996 if (extensions
&& extensions_item
.Length
)
997 *extensions
= CFDataCreate(kCFAllocatorDefault
, extensions_item
.Data
, extensions_item
.Length
);
998 if (challenge
&& challenge_item
.Length
) {
999 SecAsn1Item string
= { 0 };
1000 SECStatus rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1UTF8StringTemplate
, &challenge_item
);
1002 rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1PrintableStringTemplate
, &challenge_item
);
1004 *challenge
= CFStringCreateWithBytes(kCFAllocatorDefault
, string
.Data
, string
.Length
, kCFStringEncodingUTF8
, false);
1009 *publicKey
= candidatePublicKey
;
1010 candidatePublicKey
= NULL
;
1014 CFReleaseSafe(candidatePublicKey
);
1015 CFReleaseNull(keyAttrs
);
1016 CFReleaseNull(keyData
);
1017 CFReleaseNull(hash
);
1018 CFReleaseNull(signature
);
1020 PORT_FreeArena(poolp
, PR_TRUE
);
1024 #define HIDIGIT(v) (((v) / 10) + '0')
1025 #define LODIGIT(v) (((v) % 10) + '0')
1028 DER_CFDateToUTCTime(PRArenaPool
*poolp
, CFAbsoluteTime date
, SecAsn1Item
* utcTime
)
1032 utcTime
->Length
= 13;
1033 utcTime
->Data
= d
= PORT_ArenaAlloc(poolp
, 13);
1037 __block
int year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
1038 __block
bool result
;
1039 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
1040 result
= CFCalendarDecomposeAbsoluteTime(zuluCalendar
, date
, "yMdHms", &year
, &month
, &day
, &hour
, &minute
, &second
);
1045 /* UTC time does not handle the years before 1950 */
1049 /* remove the century since it's added to the year by the
1050 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
1053 d
[0] = HIDIGIT(year
);
1054 d
[1] = LODIGIT(year
);
1055 d
[2] = HIDIGIT(month
);
1056 d
[3] = LODIGIT(month
);
1057 d
[4] = HIDIGIT(day
);
1058 d
[5] = LODIGIT(day
);
1059 d
[6] = HIDIGIT(hour
);
1060 d
[7] = LODIGIT(hour
);
1061 d
[8] = HIDIGIT(minute
);
1062 d
[9] = LODIGIT(minute
);
1063 d
[10] = HIDIGIT(second
);
1064 d
[11] = LODIGIT(second
);
1070 SecGenerateSelfSignedCertificate(CFArrayRef subject
, CFDictionaryRef parameters
,
1071 SecKeyRef publicKey
, SecKeyRef privateKey
)
1073 SecCertificateRef cert
= NULL
;
1074 PRArenaPool
*poolp
= PORT_NewArena(1024);
1075 CFDictionaryRef pubkey_attrs
= NULL
;
1076 CFDataRef publicKeyData
= NULL
;
1077 uint8_t *signature
= NULL
;
1081 NSS_Certificate cert_tmpl
;
1082 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
1085 unsigned char version
= 2;
1086 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
1087 cert_tmpl
.tbs
.version
.Data
= &version
;
1090 unsigned char serialNumber
= 1;
1091 cert_tmpl
.tbs
.serialNumber
.Length
= sizeof(serialNumber
);
1092 cert_tmpl
.tbs
.serialNumber
.Data
= &serialNumber
;
1094 /* subject/issuer */
1095 cert_tmpl
.tbs
.issuer
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
1096 cert_tmpl
.tbs
.subject
.rdns
= cert_tmpl
.tbs
.issuer
.rdns
;
1098 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
1099 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
1100 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
1101 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
1104 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, parameters
);
1106 /* @@@ we only handle rsa keys */
1107 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
1108 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
1109 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
1110 /* public key data and algorithm */
1111 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
1112 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
1114 publicKeyData
= SecKeyCopyExternalRepresentation(publicKey
, NULL
);
1115 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(publicKeyData
);
1116 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(publicKeyData
);
1118 /* signature algorithm */
1119 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
1120 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
1121 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
1122 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
1124 /* encode request info by itself to calculate signature */
1125 SecAsn1Item tbscert
= {};
1126 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
1128 /* calculate signature */
1129 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
1130 CCDigest(kCCDigestSHA1
, tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
1131 CFDataRef digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, tbscert_hash
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
1132 CFDataRef sigData
= SecKeyCreateSignature(privateKey
, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
,
1134 CFReleaseNull(digest
);
1135 require_quiet(sigData
, out
);
1136 size_t signature_length
= (size_t)CFDataGetLength(sigData
);
1137 signature
= malloc(signature_length
);
1138 memcpy(signature
, CFDataGetBytePtr(sigData
), CFDataGetLength(sigData
));
1139 CFReleaseNull(sigData
);
1142 cert_tmpl
.signature
.Data
= signature
;
1143 cert_tmpl
.signature
.Length
= signature_length
* 8;
1146 SecAsn1Item signed_cert
= {};
1147 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1148 kSecAsn1SignedCertTemplate
), out
);
1149 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1150 signed_cert
.Data
, signed_cert
.Length
);
1154 PORT_FreeArena(poolp
, PR_TRUE
);
1155 CFReleaseSafe(pubkey_attrs
);
1156 CFReleaseNull(publicKeyData
);
1157 if (signature
) { free(signature
); }
1163 SecIdentitySignCertificate(SecIdentityRef issuer
, CFDataRef serialno
,
1164 SecKeyRef publicKey
, CFTypeRef subject
, CFTypeRef extensions
)
1166 SecCertificateRef cert
= NULL
;
1167 SecKeyRef privateKey
= NULL
;
1168 uint8_t *signature
= NULL
;
1170 PRArenaPool
*poolp
= PORT_NewArena(1024);
1171 CFDataRef publicKeyData
= NULL
;
1175 NSS_Certificate cert_tmpl
;
1176 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
1179 unsigned char version
= 2;
1180 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
1181 cert_tmpl
.tbs
.version
.Data
= &version
;
1184 cert_tmpl
.tbs
.serialNumber
.Length
= CFDataGetLength(serialno
);
1185 cert_tmpl
.tbs
.serialNumber
.Data
= (uint8_t*)CFDataGetBytePtr(serialno
);
1187 /* subject/issuer */
1188 if (CFArrayGetTypeID() == CFGetTypeID(subject
))
1189 cert_tmpl
.tbs
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
1190 else if (CFDataGetTypeID() == CFGetTypeID(subject
)) {
1191 SecAsn1Item subject_item
= { CFDataGetLength(subject
), (uint8_t*)CFDataGetBytePtr(subject
) };
1192 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.subject
.rdns
, kSecAsn1NameTemplate
, &subject_item
), out
);
1196 SecCertificateRef issuer_cert
= NULL
;
1197 require_noerr(SecIdentityCopyCertificate(issuer
, &issuer_cert
), out
);
1198 CFDataRef issuer_name
= SecCertificateCopySubjectSequence(issuer_cert
);
1199 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_name
), (uint8_t*)CFDataGetBytePtr(issuer_name
) };
1200 require_noerr_action_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.issuer
.rdns
,
1201 kSecAsn1NameTemplate
, &issuer_item
), out
, CFReleaseNull(issuer_name
));
1202 CFReleaseNull(issuer_name
);
1204 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
1205 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
1206 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
1207 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
1211 if (CFDataGetTypeID() == CFGetTypeID(extensions
)) {
1212 SecAsn1Item requested_extensions
= { CFDataGetLength(extensions
), (uint8_t*)CFDataGetBytePtr(extensions
) };
1213 //NSS_CertExtension **requested_extensions_decoded;
1214 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.extensions
,
1215 kSecAsn1SequenceOfCertExtensionTemplate
, &requested_extensions
), out
);
1216 } else if (CFDictionaryGetTypeID() == CFGetTypeID(extensions
)) {
1217 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, extensions
);
1221 /* @@@ we only handle rsa keys */
1222 if (SecKeyGetAlgorithmId(publicKey
) == kSecRSAAlgorithmID
) {
1223 /* public key data and algorithm */
1224 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
1225 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
1227 publicKeyData
= SecKeyCopyExternalRepresentation(publicKey
, NULL
);
1228 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(publicKeyData
);
1229 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(publicKeyData
);
1231 /* signature algorithm */
1232 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
1233 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
1234 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
1235 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
1237 /* encode request info by itself to calculate signature */
1238 SecAsn1Item tbscert
= {};
1239 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
1241 /* calculate signature */
1242 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
1243 CCDigest(kCCDigestSHA1
, tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
1245 require_noerr_quiet(SecIdentityCopyPrivateKey(issuer
, &privateKey
), out
);
1246 CFDataRef digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, tbscert_hash
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
1247 CFDataRef sigData
= SecKeyCreateSignature(privateKey
, kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
,
1249 CFReleaseNull(digest
);
1250 require_quiet(sigData
, out
);
1251 size_t signature_length
= (size_t)CFDataGetLength(sigData
);
1252 signature
= malloc(signature_length
);
1253 memcpy(signature
, CFDataGetBytePtr(sigData
), CFDataGetLength(sigData
));
1254 CFReleaseNull(sigData
);
1257 cert_tmpl
.signature
.Data
= signature
;
1258 cert_tmpl
.signature
.Length
= signature_length
* 8;
1261 SecAsn1Item signed_cert
= {};
1262 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1263 kSecAsn1SignedCertTemplate
), out
);
1264 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1265 signed_cert
.Data
, signed_cert
.Length
);
1268 CFReleaseSafe(privateKey
);
1270 PORT_FreeArena(poolp
, PR_TRUE
);
1271 CFReleaseSafe(publicKeyData
);
1272 if (signature
) { free(signature
); }
1278 SecGenerateCertificateRequestSubject(SecCertificateRef ca_certificate
, CFArrayRef subject
)
1280 CFMutableDataRef sequence
= NULL
;
1281 PRArenaPool
*poolp
= PORT_NewArena(1024);
1286 Going agains the spec here:
1288 3.2.3. GetCertInitial
1290 The messageData for this type consists of a DER-encoded
1291 IssuerAndSubject (Section 3.2.3.1). The issuer is set to the
1292 issuerName from the certification authority from which we are issued
1293 certificates. The Subject is set to the SubjectName we used when
1294 requesting the certificate.
1296 That clearly says use the issuer of the cert issuing certificate. Since
1297 it is combined with the subject of the to-be-issued certificate, that
1298 seems a mistake. If we take the subject of the issuer and the subject
1299 of the certificate we're interested in, we get the issuer and subject
1300 the certificate to be returned will have.
1303 CFDataRef issuer_sequence
= SecCertificateCopySubjectSequence(ca_certificate
);
1304 SecAsn1Item subject_item
= { 0 };
1305 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_sequence
), (uint8_t*)CFDataGetBytePtr(issuer_sequence
) };
1306 NSS_Name nss_subject
= { make_subject(poolp
, subject
) };
1307 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
, &nss_subject
, kSecAsn1NameTemplate
), out
);
1309 DERSize sequence_length
= DERLengthOfLength(subject_item
.Length
+ issuer_item
.Length
);
1310 DERSize seq_len_length
= subject_item
.Length
+ issuer_item
.Length
+ 1 /* SEQUENCE */ +
1312 sequence
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
1313 CFDataSetLength(sequence
, seq_len_length
);
1314 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1315 *sequence_ptr
++ = 0x30; //ONE_BYTE_ASN1_CONSTR_SEQUENCE;
1316 require_noerr_quiet(DEREncodeLength(subject_item
.Length
+ issuer_item
.Length
, sequence_ptr
, &sequence_length
), out
);
1317 sequence_ptr
+= sequence_length
;
1318 memcpy(sequence_ptr
, issuer_item
.Data
, issuer_item
.Length
);
1319 memcpy(sequence_ptr
+ issuer_item
.Length
, subject_item
.Data
, subject_item
.Length
);
1322 CFReleaseSafe(issuer_sequence
);
1324 PORT_FreeArena(poolp
, PR_TRUE
);