2 * Copyright (c) 2008-2009 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 <AssertMacros.h>
60 #include "SecCertificateRequest.h"
62 CFTypeRef kSecOidCommonName
= CFSTR("CN");
63 CFTypeRef kSecOidCountryName
= CFSTR("C");
64 CFTypeRef kSecOidStateProvinceName
= CFSTR("ST");
65 CFTypeRef kSecOidLocalityName
= CFSTR("L");
66 CFTypeRef kSecOidOrganization
= CFSTR("O");
67 CFTypeRef kSecOidOrganizationalUnit
= CFSTR("OU");
68 //CFTypeRef kSecOidEmailAddress = CFSTR("1.2.840.113549.1.9.1");
69 // keep natural order: C > ST > L > O > OU > CN > Email
71 const unsigned char SecASN1PrintableString
= SEC_ASN1_PRINTABLE_STRING
;
72 const unsigned char SecASN1UTF8String
= SEC_ASN1_UTF8_STRING
;
74 static uint8_t * mod128_oid_encoding_ptr(uint8_t *ptr
, uint32_t src
, bool final
)
77 ptr
= mod128_oid_encoding_ptr(ptr
, src
/ 128, false);
79 unsigned char octet
= src
% 128;
87 static uint8_t * oid_der_data(PRArenaPool
*poolp
, CFStringRef oid_string
, size_t *oid_data_len
)
89 /* estimate encoded length from base 10 (4 bits) to base 128 (7 bits) */
90 size_t tmp_oid_length
= ((CFStringGetLength(oid_string
) * 4) / 7) + 1;
91 uint8_t *tmp_oid_data
= PORT_ArenaAlloc(poolp
, tmp_oid_length
);
92 uint8_t *tmp_oid_data_ptr
= tmp_oid_data
;
94 CFArrayRef oid
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
,
95 oid_string
, CFSTR("."));
96 CFIndex i
= 0, count
= CFArrayGetCount(oid
);
97 SInt32 first_digit
= 0, digit
;
98 for (i
= 0; i
< count
; i
++) {
99 CFStringRef oid_octet
= CFArrayGetValueAtIndex(oid
, i
);
100 SInt32 oid_octet_int_value
= CFStringGetIntValue(oid_octet
);
101 require(abs(oid_octet_int_value
) != INT32_MAX
, out
);
103 first_digit
= oid_octet_int_value
;
106 digit
= 40 * first_digit
+ oid_octet_int_value
;
108 digit
= oid_octet_int_value
;
109 tmp_oid_data_ptr
= mod128_oid_encoding_ptr(tmp_oid_data_ptr
, digit
, true);
114 *oid_data_len
= tmp_oid_data_ptr
- tmp_oid_data
;
122 Get challenge password conversion and apply this:
124 ASCII ? => PrintableString subset: [A-Za-z0-9 '()+,-./:=?] ?
126 PrintableString > IA5String > UTF8String
128 Consider using IA5String for email address
131 static inline bool printable_string(CFStringRef string
)
135 CFCharacterSetRef printable_charset
=
136 CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
,
137 CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
138 "abcdefghijklmnopqrstuvwxyz"
139 "0123456789 '()+,-./:=?"));
140 CFCharacterSetRef not_printable_charset
=
141 CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, printable_charset
);
143 if (CFStringFindCharacterFromSet(string
, not_printable_charset
,
144 CFRangeMake(0, CFStringGetLength(string
)), 0, &found
))
147 CFReleaseSafe(printable_charset
);
148 CFReleaseSafe(not_printable_charset
);
153 static bool make_nss_atv(PRArenaPool
*poolp
,
154 const void * oid
, const void * value
, const unsigned char type_in
, NSS_ATV
*nss_atv
)
158 unsigned char type
= type_in
;
159 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
160 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
161 kCFStringEncodingUTF8
);
162 buffer
= PORT_ArenaAlloc(poolp
, length
);
163 /* TODO: Switch to using CFStringGetBytes,since this code will do the wrong thing for embedded 0's */
164 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingASCII
)) {
165 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
167 if (type
&& type
!= SecASN1UTF8String
)
169 type
= SecASN1UTF8String
;
172 if (!type
|| type
== SecASN1PrintableString
) {
173 if (!printable_string(value
))
174 type
= SEC_ASN1_IA5_STRING
;
176 type
= SEC_ASN1_PRINTABLE_STRING
;
179 length
= strlen(buffer
);
181 else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
182 /* will remain valid for the duration of the operation, still maybe copy into pool */
183 length
= CFDataGetLength(value
);
184 buffer
= (char *)CFDataGetBytePtr(value
);
186 size_t oid_length
= 0;
187 uint8_t *oid_data
= NULL
;
188 if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
189 if (CFEqual(kSecOidCommonName
, oid
)) {
190 oid_length
= oidCommonName
.length
; oid_data
= oidCommonName
.data
;
191 } else if (CFEqual(kSecOidCountryName
, oid
)) {
192 oid_length
= oidCountryName
.length
; oid_data
= oidCountryName
.data
;
193 } else if (CFEqual(kSecOidStateProvinceName
, oid
)) {
194 oid_length
= oidStateOrProvinceName
.length
; oid_data
= oidStateOrProvinceName
.data
;
195 } else if (CFEqual(kSecOidLocalityName
, oid
)) {
196 oid_length
= oidLocalityName
.length
; oid_data
= oidLocalityName
.data
;
197 } else if (CFEqual(kSecOidOrganization
, oid
)) {
198 oid_length
= oidOrganizationName
.length
; oid_data
= oidOrganizationName
.data
;
199 } else if (CFEqual(kSecOidOrganizationalUnit
, oid
)) {
200 oid_length
= oidOrganizationalUnitName
.length
; oid_data
= oidOrganizationalUnitName
.data
;
202 oid_data
= oid_der_data(poolp
, oid
, &oid_length
);
203 require(oid_data
, out
);
205 } else if (CFGetTypeID(oid
) == CFDataGetTypeID()) {
206 /* will remain valid for the duration of the operation, still maybe copy into pool */
207 oid_length
= CFDataGetLength(oid
);
208 oid_data
= (uint8_t *)CFDataGetBytePtr(oid
);
210 NSS_ATV stage_nss_atv
= { { oid_length
, oid_data
},
211 { { length
, (uint8_t*)buffer
}, type
} };
212 *nss_atv
= stage_nss_atv
;
218 static NSS_RDN
**make_subject(PRArenaPool
*poolp
, CFArrayRef subject
)
222 CFIndex rdn_ix
, rdn_count
= CFArrayGetCount(subject
);
223 NSS_RDN
**rdnps
= PORT_ArenaZNewArray(poolp
, NSS_RDN
*, rdn_count
+ 1);
224 NSS_RDN
*rdns
= PORT_ArenaZNewArray(poolp
, NSS_RDN
, rdn_count
);
225 for (rdn_ix
= 0; rdn_ix
< rdn_count
; rdn_ix
++) {
226 rdnps
[rdn_ix
] = &rdns
[rdn_ix
];
227 CFArrayRef rdn
= CFArrayGetValueAtIndex(subject
, rdn_ix
);
228 CFIndex atv_ix
, atv_count
= CFArrayGetCount(rdn
);
229 rdns
[rdn_ix
].atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
*, atv_count
+ 1);
230 NSS_ATV
*atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
, atv_count
);
231 for (atv_ix
= 0; atv_ix
< atv_count
; atv_ix
++) {
232 rdns
[rdn_ix
].atvs
[atv_ix
] = &atvs
[atv_ix
];
233 CFArrayRef atv
= CFArrayGetValueAtIndex(rdn
, atv_ix
);
234 if ((CFArrayGetCount(atv
) != 2)
235 || !make_nss_atv(poolp
, CFArrayGetValueAtIndex(atv
, 0),
236 CFArrayGetValueAtIndex(atv
, 1), 0, &atvs
[atv_ix
]))
243 struct make_general_names_context
{
250 static void make_general_names(const void *key
, const void *value
, void *context
)
252 struct make_general_names_context
*gn
= (struct make_general_names_context
*)context
;
254 CFArrayRef gn_values
= NULL
;
255 CFStringRef gn_value
= NULL
;
256 CFIndex entry_ix
, entry_count
= 0;
257 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
258 gn_values
= (CFArrayRef
)value
;
259 entry_count
= CFArrayGetCount(value
);
260 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
261 gn_value
= (CFStringRef
)value
;
265 require(entry_count
> 0, out
);
268 require(CFGetTypeID(key
) == CFStringGetTypeID(), out
);
270 if (!gn
->names
|| (gn
->count
== gn
->capacity
)) {
271 uint32_t capacity
= gn
->capacity
;
277 void * new_array
= PORT_ArenaZNewArray(gn
->poolp
, SecAsn1Item
, capacity
);
279 memcpy(new_array
, gn
->names
, gn
->capacity
);
280 gn
->names
= new_array
;
281 gn
->capacity
= capacity
;
284 NSS_GeneralName general_name_item
= { { }, -1 };
285 if (kCFCompareEqualTo
== CFStringCompare(CFSTR("dNSName"), key
, kCFCompareCaseInsensitive
))
286 general_name_item
.tag
= NGT_DNSName
;
287 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("rfc822Name"), key
, kCFCompareCaseInsensitive
))
288 general_name_item
.tag
= NGT_RFC822Name
;
289 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("uniformResourceIdentifier"), key
, kCFCompareCaseInsensitive
))
290 general_name_item
.tag
= NGT_URI
;
291 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("ntPrincipalName"), key
, kCFCompareCaseInsensitive
))
294 NT Principal in SubjectAltName is defined in the context of Smartcards:
296 http://www.oid-info.com/get/1.3.6.1.4.1.311.20.2.3
297 http://support.microsoft.com/default.aspx?scid=kb;en-us;281245
299 Subject Alternative Name = Other Name: Principal Name= (UPN). For example:
301 The UPN OtherName OID is : "1.3.6.1.4.1.311.20.2.3"
302 The UPN OtherName value: Must be ASN1-encoded UTF8 string
303 Subject = Distinguished name of user. This field is a mandatory extension, but the population of this field is optional.
306 /* OtherName ::= SEQUENCE {
307 type-id OBJECT IDENTIFIER,
308 value [0] EXPLICIT ANY DEFINED BY type-id
310 uint8_t nt_principal_oid
[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03 };
314 } nt_principal_other_name
;
315 nt_principal_other_name name
= {};
317 const SecAsn1Template my_other_name_template
[] = {
319 0, NULL
, sizeof(nt_principal_other_name
) },
320 { SEC_ASN1_OBJECT_ID
,
321 offsetof(nt_principal_other_name
,typeId
), },
322 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| 0, offsetof(nt_principal_other_name
,value
), kSecAsn1UTF8StringTemplate
, },
325 const SecAsn1Template my_other_name_template_cons
[] = {
326 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| NGT_OtherName
,
327 0, my_other_name_template
, sizeof(nt_principal_other_name
) }
333 require(gn_value
, out
);
334 require(CFGetTypeID(gn_value
) == CFStringGetTypeID(), out
);
335 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
336 kCFStringEncodingUTF8
);
337 buffer
= PORT_ArenaAlloc(gn
->poolp
, length
);
338 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
341 name
.typeId
.Length
= sizeof(nt_principal_oid
);
342 name
.typeId
.Data
= nt_principal_oid
;
343 name
.value
.Length
= strlen(buffer
);
344 name
.value
.Data
= (uint8_t*)buffer
;
345 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &name
, my_other_name_template_cons
);
348 /* We already encoded the value for the general name */
355 for (entry_ix
= 0; entry_ix
< entry_count
; entry_ix
++) {
356 CFTypeRef entry_value
= CFArrayGetValueAtIndex(gn_values
, entry_ix
);
357 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
)entry_value
),
358 kCFStringEncodingUTF8
); /* we only allow ASCII => only expect IA5Strings */
359 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
360 require(CFStringGetCString((CFStringRef
)entry_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
361 general_name_item
.item
.Data
= (uint8_t*)buffer
;
362 general_name_item
.item
.Length
= strlen(buffer
);
363 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
366 } else if (gn_value
) {
367 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(gn_value
),
368 kCFStringEncodingUTF8
);
369 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
370 require(CFStringGetCString(gn_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
371 general_name_item
.item
.Data
= (uint8_t*)buffer
;
372 general_name_item
.item
.Length
= strlen(buffer
);
373 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
380 static SecAsn1Item
make_subjectAltName_extension(PRArenaPool
*poolp
, CFDictionaryRef subjectAltNames
)
382 SecAsn1Item subjectAltExt
= {};
384 struct make_general_names_context context
= { poolp
, NULL
, 0 };
385 CFDictionaryApplyFunction(subjectAltNames
, make_general_names
, &context
);
387 // all general names in a sequence:
389 SecAsn1Item
**general_names
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, context
.count
+ 1);
390 for (ix
= 0; ix
< context
.count
; ix
++)
391 general_names
[ix
] = &context
.names
[ix
];
392 NSS_GeneralNames gnames
= { general_names
};
393 SEC_ASN1EncodeItem(poolp
, &subjectAltExt
, &gnames
, kSecAsn1GeneralNamesTemplate
);
395 return subjectAltExt
;
398 CFTypeRef kSecCSRChallengePassword
= CFSTR("csrChallengePassword");
399 CFTypeRef kSecSubjectAltName
= CFSTR("subjectAltName");
400 CFTypeRef kSecCertificateKeyUsage
= CFSTR("keyUsage");
401 CFTypeRef kSecCSRBasicContraintsPathLen
= CFSTR("basicConstraints");
402 CFTypeRef kSecCertificateExtensions
= CFSTR("certificateExtensions");
404 static const uint8_t pkcs9ExtensionsRequested
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 14 };
405 static const uint8_t pkcs9ChallengePassword
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 7 };
407 static const uint8_t encoded_asn1_true
= 0xFF;
408 static const SecAsn1Item asn1_true
=
409 { sizeof(encoded_asn1_true
), (uint8_t*)&encoded_asn1_true
};
411 static inline uint32_t highest_bit(uint32_t n
)
413 return ((n
) >> 16 ? ((n
)>>=16, 16) : 0) + \
414 ((n
) >> 8 ? ((n
)>>=8, 8) : 0) + \
415 ((n
) >> 4 ? ((n
)>>=4, 4) : 0) + \
416 ((n
) >> 2 ? ((n
)>>=2, 2) : 0) + \
417 ((n
) >> 1 ? ((n
)>>=1, 1) : 0) + \
421 struct add_custom_extension_args
{
423 NSS_CertExtension
*csr_extension
;
424 uint32_t num_extensions
;
425 uint32_t max_extensions
;
428 static void add_custom_extension(const void *key
, const void *value
, void *context
)
430 struct add_custom_extension_args
*args
= (struct add_custom_extension_args
*)context
;
433 require(args
->num_extensions
< args
->max_extensions
, out
);
435 uint8_t * der_data
= oid_der_data(args
->poolp
, key
, &der_data_len
);
436 SecAsn1Item encoded_value
= {};
438 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
439 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
), kCFStringEncodingUTF8
);
440 char *buffer
= (char *)PORT_ArenaZNewArray(args
->poolp
, uint8_t, buffer_size
);
441 if (!CFStringGetCString(value
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
444 SecAsn1Item buffer_item
= { strlen(buffer
), (uint8_t*)buffer
};
445 SEC_ASN1EncodeItem(args
->poolp
, &encoded_value
, &buffer_item
, kSecAsn1UTF8StringTemplate
);
446 } else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
447 SecAsn1Item data_item
= { CFDataGetLength(value
), (uint8_t*)CFDataGetBytePtr(value
) };
448 SEC_ASN1EncodeItem(args
->poolp
, &encoded_value
, &data_item
, kSecAsn1OctetStringTemplate
);
453 if (der_data
&& encoded_value
.Length
) {
454 args
->csr_extension
[args
->num_extensions
].value
= encoded_value
;
455 args
->csr_extension
[args
->num_extensions
].extnId
.Length
= der_data_len
;
456 args
->csr_extension
[args
->num_extensions
].extnId
.Data
= der_data
;
457 args
->num_extensions
++;
465 extensions_from_parameters(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
467 uint32_t num_extensions
= 0, max_extensions
= 10;
468 NSS_CertExtension
**csr_extensions
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
*, max_extensions
+ 1); /* NULL terminated array */
469 NSS_CertExtension
*csr_extension
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
, max_extensions
);
471 CFNumberRef basic_contraints_num
= CFDictionaryGetValue(parameters
, kSecCSRBasicContraintsPathLen
);
472 if (basic_contraints_num
) {
473 NSS_BasicConstraints basic_contraints
= { asn1_true
, {} };
476 int basic_contraints_path_len
= 0;
477 require(CFNumberGetValue(basic_contraints_num
, kCFNumberIntType
, &basic_contraints_path_len
), out
);
478 if (basic_contraints_path_len
>= 0 && basic_contraints_path_len
< 256) {
479 path_len
= (uint8_t)basic_contraints_path_len
;
480 basic_contraints
.pathLenConstraint
.Length
= sizeof(path_len
);
481 basic_contraints
.pathLenConstraint
.Data
= &path_len
;
484 csr_extension
[num_extensions
].extnId
.Data
= oidBasicConstraints
.data
;
485 csr_extension
[num_extensions
].extnId
.Length
= oidBasicConstraints
.length
;
486 csr_extension
[num_extensions
].critical
= asn1_true
;
488 SEC_ASN1EncodeItem(poolp
, &csr_extension
[num_extensions
].value
, &basic_contraints
,
489 kSecAsn1BasicConstraintsTemplate
);
490 require(num_extensions
++ < max_extensions
, out
);
493 CFDictionaryRef subject_alternate_names
= CFDictionaryGetValue(parameters
, kSecSubjectAltName
);
494 if (subject_alternate_names
) {
495 require(CFGetTypeID(subject_alternate_names
) == CFDictionaryGetTypeID(), out
);
496 csr_extension
[num_extensions
].value
= make_subjectAltName_extension(poolp
, subject_alternate_names
);
497 /* set up subjectAltName cert request value */
498 csr_extension
[num_extensions
].extnId
.Length
= oidSubjectAltName
.length
;
499 csr_extension
[num_extensions
].extnId
.Data
= oidSubjectAltName
.data
;
500 require(num_extensions
++ < max_extensions
, out
);
503 CFNumberRef key_usage_requested
= CFDictionaryGetValue(parameters
, kSecCertificateKeyUsage
);
504 SecAsn1Item key_usage_asn1_value
= { 0 };
505 if (key_usage_requested
) {
507 require(CFNumberGetValue(key_usage_requested
, kCFNumberIntType
, &key_usage_value
), out
);
508 if (key_usage_value
> 0) {
509 uint32_t key_usage_value_be
= 0, key_usage_mask
= 1<<31;
510 uint32_t key_usage_value_max_bitlen
= 9, key_usage_value_bitlen
= 0;
511 while(key_usage_value_max_bitlen
) {
512 if (key_usage_value
& 1) {
513 key_usage_value_be
|= key_usage_mask
;
514 key_usage_value_bitlen
= 10 - key_usage_value_max_bitlen
;
516 key_usage_value
>>= 1;
517 key_usage_value_max_bitlen
--;
518 key_usage_mask
>>= 1;
521 SecAsn1Item key_usage_input
= { key_usage_value_bitlen
,
522 ((uint8_t*)&key_usage_value_be
) + 3 - (key_usage_value_bitlen
>> 3) };
523 SEC_ASN1EncodeItem(poolp
, &key_usage_asn1_value
, &key_usage_input
, kSecAsn1BitStringTemplate
);
525 csr_extension
[num_extensions
].extnId
.Data
= oidKeyUsage
.data
;
526 csr_extension
[num_extensions
].extnId
.Length
= oidKeyUsage
.length
;
527 csr_extension
[num_extensions
].critical
= asn1_true
;
528 csr_extension
[num_extensions
].value
= key_usage_asn1_value
;
529 require(num_extensions
++ < max_extensions
, out
);
533 CFDictionaryRef custom_extension_requested
= CFDictionaryGetValue(parameters
, kSecCertificateExtensions
);
534 if (custom_extension_requested
) {
535 require(CFGetTypeID(custom_extension_requested
) == CFDictionaryGetTypeID(), out
);
536 struct add_custom_extension_args args
= {
542 CFDictionaryApplyFunction(custom_extension_requested
, add_custom_extension
, &args
);
543 num_extensions
= args
.num_extensions
;
546 /* extensions requested (subjectAltName, keyUsage) sequence of extension sequences */
548 for (ix
= 0; ix
< num_extensions
; ix
++)
549 csr_extensions
[ix
] = csr_extension
[ix
].extnId
.Length
? &csr_extension
[ix
] : NULL
;
552 return csr_extensions
;
558 NSS_Attribute
**nss_attributes_from_parameters_dict(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
560 /* A challenge-password attribute must have a single attribute value.
562 ChallengePassword attribute values generated in accordance with this
563 version of this document SHOULD use the PrintableString encoding
564 whenever possible. If internationalization issues make this
565 impossible, the UTF8String alternative SHOULD be used. PKCS #9-
566 attribute processing systems MUST be able to recognize and process
567 all string types in DirectoryString values.
569 Upperbound of 255 defined for all PKCS#9 attributes.
571 pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840)
572 rsadsi(113549) pkcs(1) 9}
573 pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7}
578 uint32_t num_attrs
= 0;
580 CFStringRef challenge
= CFDictionaryGetValue(parameters
, kSecCSRChallengePassword
);
581 NSS_Attribute challenge_password_attr
= {};
583 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(challenge
),
584 kCFStringEncodingUTF8
); /* we only allow UTF8 or ASCII */
585 char *buffer
= (char *)PORT_ArenaZNewArray(poolp
, uint8_t, buffer_size
);
587 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingASCII
)) {
588 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
592 if (!printable_string(challenge
))
595 SecAsn1Item
*challenge_password_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
596 SecAsn1Item challenge_password_raw
= { strlen(buffer
), (uint8_t*)buffer
};
597 SEC_ASN1EncodeItem(poolp
, challenge_password_value
, &challenge_password_raw
,
598 utf8
? kSecAsn1UTF8StringTemplate
: kSecAsn1PrintableStringTemplate
);
599 SecAsn1Item
**challenge_password_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
600 challenge_password_values
[0] = challenge_password_value
;
601 challenge_password_attr
.attrType
.Length
= sizeof(pkcs9ChallengePassword
);
602 challenge_password_attr
.attrType
.Data
= (uint8_t*)&pkcs9ChallengePassword
;
603 challenge_password_attr
.attrValue
= challenge_password_values
;
607 NSS_CertExtension
**extensions
= extensions_from_parameters(poolp
, parameters
);
608 NSS_Attribute extensions_requested_attr
= {};
610 SecAsn1Item
*extensions_requested_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
611 SEC_ASN1EncodeItem(poolp
, extensions_requested_value
, &extensions
, kSecAsn1SequenceOfCertExtensionTemplate
);
612 SecAsn1Item
**extensions_requested_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
613 extensions_requested_values
[0] = extensions_requested_value
;
614 extensions_requested_values
[1] = NULL
;
615 extensions_requested_attr
.attrType
.Length
= sizeof(pkcs9ExtensionsRequested
);
616 extensions_requested_attr
.attrType
.Data
= (uint8_t*)pkcs9ExtensionsRequested
;
617 extensions_requested_attr
.attrValue
= extensions_requested_values
;
621 NSS_Attribute
**attributes_ptr
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
*, num_attrs
+ 1);
622 NSS_Attribute
*attributes
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
, num_attrs
);
623 if (challenge_password_attr
.attrType
.Length
) {
625 attributes
[num_attrs
] = challenge_password_attr
;
626 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
628 if (extensions_requested_attr
.attrType
.Length
) {
630 attributes
[num_attrs
] = extensions_requested_attr
;
631 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
633 return attributes_ptr
;
640 static const uint8_t encoded_null
[2] = { SEC_ASN1_NULL
, 0 };
641 static const SecAsn1Item asn1_null
= { sizeof(encoded_null
), (uint8_t*)encoded_null
};
643 CFDataRef
SecGenerateCertificateRequestWithParameters(SecRDN
*subject
,
644 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
646 CFDataRef csr
= NULL
;
647 PRArenaPool
*poolp
= PORT_NewArena(1024);
648 CFDictionaryRef pubkey_attrs
= NULL
;
653 NSSCertRequest certReq
;
654 memset(&certReq
, 0, sizeof(certReq
));
657 unsigned char version
= 0;
658 certReq
.reqInfo
.version
.Length
= sizeof(version
);
659 certReq
.reqInfo
.version
.Data
= &version
;
662 unsigned atv_num
= 0, num
= 0;
665 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
666 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++)
668 atv_num
++; /* one more */
671 NSS_ATV atvs
[atv_num
];
672 NSS_ATV
*atvps
[atv_num
];
674 NSS_RDN
*rdnps
[num
+1];
676 unsigned rdn_num
= 0;
677 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
678 rdns
[rdn_num
].atvs
= &atvps
[atv_num
];
679 rdnps
[rdn_num
] = &rdns
[rdn_num
];
681 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++) {
682 if (!make_nss_atv(poolp
, one_atv
->oid
, one_atv
->value
,
683 one_atv
->type
, &atvs
[atv_num
]))
685 atvps
[atv_num
] = &atvs
[atv_num
];
688 atvps
[atv_num
++] = NULL
;
690 rdnps
[rdn_num
] = NULL
;
691 certReq
.reqInfo
.subject
.rdns
= rdnps
;
693 /* public key info */
694 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
695 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
696 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
698 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
699 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
700 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
701 size_t signature_length
= sizeof(signature
);
703 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
704 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
706 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
707 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
709 /* encode request info by itself to calculate signature */
710 SecAsn1Item reqinfo
= {};
711 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
713 /* calculate signature */
714 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
715 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
716 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
717 reqinfo_hash
, sizeof(reqinfo_hash
), signature
, &signature_length
), out
);
719 /* signature and info */
720 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha1Rsa
.length
;
721 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha1Rsa
.data
;
722 certReq
.signatureAlgorithm
.parameters
= asn1_null
;
723 certReq
.signature
.Data
= signature
;
724 certReq
.signature
.Length
= signature_length
* 8;
727 SecAsn1Item cert_request
= {};
728 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
729 kSecAsn1CertRequestTemplate
), out
);
730 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
734 PORT_FreeArena(poolp
, PR_TRUE
);
735 CFReleaseSafe(pubkey_attrs
);
739 CFDataRef
SecGenerateCertificateRequest(CFArrayRef subject
,
740 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
742 CFDataRef csr
= NULL
;
743 PRArenaPool
*poolp
= PORT_NewArena(1024);
744 CFDictionaryRef pubkey_attrs
= NULL
;
749 NSSCertRequest certReq
;
750 memset(&certReq
, 0, sizeof(certReq
));
753 unsigned char version
= 0;
754 certReq
.reqInfo
.version
.Length
= sizeof(version
);
755 certReq
.reqInfo
.version
.Data
= &version
;
758 certReq
.reqInfo
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
760 /* public key info */
761 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
762 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
763 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
765 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
766 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
767 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
768 size_t signature_length
= sizeof(signature
);
770 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
771 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
773 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
774 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
776 /* encode request info by itself to calculate signature */
777 SecAsn1Item reqinfo
= {};
778 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
780 /* calculate signature */
781 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
782 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, reqinfo
.Length
, reqinfo_hash
);
783 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
784 reqinfo_hash
, sizeof(reqinfo_hash
), signature
, &signature_length
), out
);
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;
794 SecAsn1Item cert_request
= {};
795 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
796 kSecAsn1CertRequestTemplate
), out
);
797 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
801 PORT_FreeArena(poolp
, PR_TRUE
);
802 CFReleaseSafe(pubkey_attrs
);
806 bool SecVerifyCertificateRequest(CFDataRef csr
, SecKeyRef
*publicKey
,
807 CFStringRef
*challenge
, CFDataRef
*subject
, CFDataRef
*extensions
)
809 PRArenaPool
*poolp
= PORT_NewArena(1024);
810 SecKeyRef candidatePublicKey
= NULL
;
812 NSSCertRequest certReq
;
813 memset(&certReq
, 0, sizeof(certReq
));
814 SecAsn1Item csr_item
= { CFDataGetLength(csr
), (uint8_t*)CFDataGetBytePtr(csr
) };
815 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &certReq
, kSecAsn1CertRequestTemplate
,
818 /* signature and info */
819 require(certReq
.signatureAlgorithm
.algorithm
.Length
== oidSha1Rsa
.length
, out
);
820 require_noerr(memcmp(oidSha1Rsa
.data
, certReq
.signatureAlgorithm
.algorithm
.Data
,
821 oidSha1Rsa
.length
), out
);
822 require(certReq
.signatureAlgorithm
.parameters
.Length
== asn1_null
.Length
, out
);
823 require_noerr(memcmp(asn1_null
.Data
, certReq
.signatureAlgorithm
.parameters
.Data
,
824 asn1_null
.Length
), out
);
826 /* encode request info by itself to calculate signature */
827 SecAsn1Item reqinfo
= {};
828 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
830 /* calculate signature */
831 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
832 require(reqinfo
.Length
<=UINT32_MAX
, out
);
833 CCDigest(kCCDigestSHA1
, reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
835 /* @@@ check for version 0 */
837 require(candidatePublicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
838 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
,
839 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
/ 8,
840 kSecKeyEncodingPkcs1
), out
);
842 require_noerr_quiet(SecKeyRawVerify(candidatePublicKey
, kSecPaddingPKCS1SHA1
,
843 reqinfo_hash
, sizeof(reqinfo_hash
),
844 certReq
.signature
.Data
, certReq
.signature
.Length
/ 8), out
);
846 SecAsn1Item subject_item
= { 0 }, extensions_item
= { 0 }, challenge_item
= { 0 };
847 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
,
848 &certReq
.reqInfo
.subject
, kSecAsn1NameTemplate
), out
);
850 if (*certReq
.reqInfo
.attributes
) {
852 for (ix
= 0; certReq
.reqInfo
.attributes
[ix
]; ix
++) {
853 NSS_Attribute
*attr
= certReq
.reqInfo
.attributes
[ix
];
854 if ( (sizeof(pkcs9ChallengePassword
) == attr
->attrType
.Length
) &&
855 !memcmp(pkcs9ChallengePassword
, attr
->attrType
.Data
, sizeof(pkcs9ChallengePassword
)))
856 challenge_item
= *attr
->attrValue
[0];
857 else if ( (sizeof(pkcs9ExtensionsRequested
) == attr
->attrType
.Length
) &&
858 !memcmp(pkcs9ExtensionsRequested
, attr
->attrType
.Data
, sizeof(pkcs9ExtensionsRequested
)))
859 extensions_item
= *attr
->attrValue
[0];
863 if (subject
&& subject_item
.Length
)
864 *subject
= CFDataCreate(kCFAllocatorDefault
, subject_item
.Data
, subject_item
.Length
);
865 if (extensions
&& extensions_item
.Length
)
866 *extensions
= CFDataCreate(kCFAllocatorDefault
, extensions_item
.Data
, extensions_item
.Length
);
867 if (challenge
&& challenge_item
.Length
) {
868 SecAsn1Item string
= { 0 };
869 SECStatus rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1UTF8StringTemplate
, &challenge_item
);
871 rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1PrintableStringTemplate
, &challenge_item
);
873 *challenge
= CFStringCreateWithBytes(kCFAllocatorDefault
, string
.Data
, string
.Length
, kCFStringEncodingUTF8
, false);
878 *publicKey
= candidatePublicKey
;
879 candidatePublicKey
= NULL
;
883 CFReleaseSafe(candidatePublicKey
);
885 PORT_FreeArena(poolp
, PR_TRUE
);
889 #define HIDIGIT(v) (((v) / 10) + '0')
890 #define LODIGIT(v) (((v) % 10) + '0')
893 DER_CFDateToUTCTime(PRArenaPool
*poolp
, CFAbsoluteTime date
, SecAsn1Item
* utcTime
)
895 CFGregorianDate gdate
= CFAbsoluteTimeGetGregorianDate(date
, NULL
/* GMT */);
899 utcTime
->Length
= 13;
900 utcTime
->Data
= d
= PORT_ArenaAlloc(poolp
, 13);
904 /* UTC time does not handle the years before 1950 */
905 if (gdate
.year
< 1950)
908 /* remove the century since it's added to the year by the
909 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
911 second
= gdate
.second
;
913 d
[0] = HIDIGIT(gdate
.year
);
914 d
[1] = LODIGIT(gdate
.year
);
915 d
[2] = HIDIGIT(gdate
.month
);
916 d
[3] = LODIGIT(gdate
.month
);
917 d
[4] = HIDIGIT(gdate
.day
);
918 d
[5] = LODIGIT(gdate
.day
);
919 d
[6] = HIDIGIT(gdate
.hour
);
920 d
[7] = LODIGIT(gdate
.hour
);
921 d
[8] = HIDIGIT(gdate
.minute
);
922 d
[9] = LODIGIT(gdate
.minute
);
923 d
[10] = HIDIGIT(second
);
924 d
[11] = LODIGIT(second
);
930 SecGenerateSelfSignedCertificate(CFArrayRef subject
, CFDictionaryRef parameters
,
931 SecKeyRef publicKey
, SecKeyRef privateKey
)
933 SecCertificateRef cert
= NULL
;
934 PRArenaPool
*poolp
= PORT_NewArena(1024);
935 CFDictionaryRef pubkey_attrs
= NULL
;
939 NSS_Certificate cert_tmpl
;
940 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
943 unsigned char version
= 2;
944 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
945 cert_tmpl
.tbs
.version
.Data
= &version
;
948 unsigned char serialNumber
= 1;
949 cert_tmpl
.tbs
.serialNumber
.Length
= sizeof(serialNumber
);
950 cert_tmpl
.tbs
.serialNumber
.Data
= &serialNumber
;
953 cert_tmpl
.tbs
.issuer
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
954 cert_tmpl
.tbs
.subject
.rdns
= cert_tmpl
.tbs
.issuer
.rdns
;
956 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
957 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
958 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
959 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
962 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, parameters
);
964 /* @@@ we only handle rsa keys */
965 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
966 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
967 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
968 /* public key data and algorithm */
969 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
970 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
972 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
973 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
974 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
976 /* signature algorithm */
977 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
978 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
979 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
980 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
982 /* encode request info by itself to calculate signature */
983 SecAsn1Item tbscert
= {};
984 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
986 /* calculate signature */
987 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
988 CCDigest(kCCDigestSHA1
, tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
989 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
990 size_t signature_length
= sizeof(signature
);
991 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
992 tbscert_hash
, sizeof(tbscert_hash
), signature
, &signature_length
), out
);
995 cert_tmpl
.signature
.Data
= signature
;
996 cert_tmpl
.signature
.Length
= signature_length
* 8;
999 SecAsn1Item signed_cert
= {};
1000 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1001 kSecAsn1SignedCertTemplate
), out
);
1002 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1003 signed_cert
.Data
, signed_cert
.Length
);
1007 PORT_FreeArena(poolp
, PR_TRUE
);
1008 CFReleaseSafe(pubkey_attrs
);
1014 SecIdentitySignCertificate(SecIdentityRef issuer
, CFDataRef serialno
,
1015 SecKeyRef publicKey
, CFTypeRef subject
, CFTypeRef extensions
)
1017 SecCertificateRef cert
= NULL
;
1018 SecKeyRef privateKey
= NULL
;
1020 PRArenaPool
*poolp
= PORT_NewArena(1024);
1021 CFDictionaryRef pubkey_attrs
= NULL
;
1025 NSS_Certificate cert_tmpl
;
1026 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
1029 unsigned char version
= 2;
1030 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
1031 cert_tmpl
.tbs
.version
.Data
= &version
;
1034 cert_tmpl
.tbs
.serialNumber
.Length
= CFDataGetLength(serialno
);
1035 cert_tmpl
.tbs
.serialNumber
.Data
= (uint8_t*)CFDataGetBytePtr(serialno
);
1037 /* subject/issuer */
1038 if (CFArrayGetTypeID() == CFGetTypeID(subject
))
1039 cert_tmpl
.tbs
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
1040 else if (CFDataGetTypeID() == CFGetTypeID(subject
)) {
1041 SecAsn1Item subject_item
= { CFDataGetLength(subject
), (uint8_t*)CFDataGetBytePtr(subject
) };
1042 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.subject
.rdns
, kSecAsn1NameTemplate
, &subject_item
), out
);
1046 SecCertificateRef issuer_cert
= NULL
;
1047 require_noerr(SecIdentityCopyCertificate(issuer
, &issuer_cert
), out
);
1048 CFDataRef issuer_name
= SecCertificateCopySubjectSequence(issuer_cert
);
1049 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_name
), (uint8_t*)CFDataGetBytePtr(issuer_name
) };
1050 require_noerr_action_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.issuer
.rdns
,
1051 kSecAsn1NameTemplate
, &issuer_item
), out
, CFReleaseNull(issuer_name
));
1052 CFReleaseNull(issuer_name
);
1054 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
1055 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
1056 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
1057 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
1061 if (CFDataGetTypeID() == CFGetTypeID(extensions
)) {
1062 SecAsn1Item requested_extensions
= { CFDataGetLength(extensions
), (uint8_t*)CFDataGetBytePtr(extensions
) };
1063 //NSS_CertExtension **requested_extensions_decoded;
1064 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.extensions
,
1065 kSecAsn1SequenceOfCertExtensionTemplate
, &requested_extensions
), out
);
1066 } else if (CFDictionaryGetTypeID() == CFGetTypeID(extensions
)) {
1067 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, extensions
);
1071 /* @@@ we only handle rsa keys */
1072 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
1073 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
1074 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
1075 /* public key data and algorithm */
1076 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
1077 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
1079 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
1080 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
1081 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
1083 /* signature algorithm */
1084 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
1085 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
1086 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
1087 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
1089 /* encode request info by itself to calculate signature */
1090 SecAsn1Item tbscert
= {};
1091 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
1093 /* calculate signature */
1094 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
1095 CCDigest(kCCDigestSHA1
, tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
1096 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
1097 size_t signature_length
= sizeof(signature
);
1099 require_noerr_quiet(SecIdentityCopyPrivateKey(issuer
, &privateKey
), out
);
1100 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
1101 tbscert_hash
, sizeof(tbscert_hash
), signature
, &signature_length
), out
);
1104 cert_tmpl
.signature
.Data
= signature
;
1105 cert_tmpl
.signature
.Length
= signature_length
* 8;
1108 SecAsn1Item signed_cert
= {};
1109 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1110 kSecAsn1SignedCertTemplate
), out
);
1111 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1112 signed_cert
.Data
, signed_cert
.Length
);
1115 CFReleaseSafe(privateKey
);
1117 PORT_FreeArena(poolp
, PR_TRUE
);
1118 CFReleaseSafe(pubkey_attrs
);
1123 SecGenerateCertificateRequestSubject(SecCertificateRef ca_certificate
, CFArrayRef subject
)
1125 CFMutableDataRef sequence
= NULL
;
1126 PRArenaPool
*poolp
= PORT_NewArena(1024);
1131 Going agains the spec here:
1133 3.2.3. GetCertInitial
1135 The messageData for this type consists of a DER-encoded
1136 IssuerAndSubject (Section 3.2.3.1). The issuer is set to the
1137 issuerName from the certification authority from which we are issued
1138 certificates. The Subject is set to the SubjectName we used when
1139 requesting the certificate.
1141 That clearly says use the issuer of the cert issuing certificate. Since
1142 it is combined with the subject of the to-be-issued certificate, that
1143 seems a mistake. If we take the subject of the issuer and the subject
1144 of the certificate we're interested in, we get the issuer and subject
1145 the certificate to be returned will have.
1148 CFDataRef issuer_sequence
= SecCertificateCopySubjectSequence(ca_certificate
);
1149 SecAsn1Item subject_item
= { 0 };
1150 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_sequence
), (uint8_t*)CFDataGetBytePtr(issuer_sequence
) };
1151 NSS_Name nss_subject
= { make_subject(poolp
, subject
) };
1152 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
, &nss_subject
, kSecAsn1NameTemplate
), out
);
1154 DERSize sequence_length
= DERLengthOfLength(subject_item
.Length
+ issuer_item
.Length
);
1155 DERSize seq_len_length
= subject_item
.Length
+ issuer_item
.Length
+ 1 /* SEQUENCE */ +
1157 sequence
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
1158 CFDataSetLength(sequence
, seq_len_length
);
1159 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1160 *sequence_ptr
++ = 0x30; //ASN1_CONSTR_SEQUENCE;
1161 require_noerr_quiet(DEREncodeLength(subject_item
.Length
+ issuer_item
.Length
, sequence_ptr
, &sequence_length
), out
);
1162 sequence_ptr
+= sequence_length
;
1163 memcpy(sequence_ptr
, issuer_item
.Data
, issuer_item
.Length
);
1164 memcpy(sequence_ptr
+ issuer_item
.Length
, subject_item
.Data
, subject_item
.Length
);
1167 CFReleaseSafe(issuer_sequence
);
1169 PORT_FreeArena(poolp
, PR_TRUE
);