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 <CoreFoundation/CFNumber.h>
55 #include <CoreFoundation/CFString.h>
57 #include <AssertMacros.h>
59 #include "SecCertificateRequest.h"
61 CFTypeRef kSecOidCommonName
= CFSTR("CN");
62 CFTypeRef kSecOidCountryName
= CFSTR("C");
63 CFTypeRef kSecOidStateProvinceName
= CFSTR("ST");
64 CFTypeRef kSecOidLocalityName
= CFSTR("L");
65 CFTypeRef kSecOidOrganization
= CFSTR("O");
66 CFTypeRef kSecOidOrganizationalUnit
= CFSTR("OU");
67 //CFTypeRef kSecOidEmailAddress = CFSTR("1.2.840.113549.1.9.1");
68 // keep natural order: C > ST > L > O > OU > CN > Email
70 const unsigned char SecASN1PrintableString
= SEC_ASN1_PRINTABLE_STRING
;
71 const unsigned char SecASN1UTF8String
= SEC_ASN1_UTF8_STRING
;
73 static void mod128_oid_encoding(CFMutableDataRef dst
, uint32_t src
, bool final
)
76 mod128_oid_encoding(dst
, src
/ 128, false);
78 unsigned char octet
= src
% 128;
81 CFDataAppendBytes(dst
, &octet
, 1);
84 static CFDataRef
oid_der(CFStringRef oid_string
)
86 CFMutableDataRef oid_der_data
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
87 CFArrayRef oid
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
,
88 oid_string
, CFSTR("."));
89 CFIndex i
= 0, count
= CFArrayGetCount(oid
);
90 SInt32 first_digit
= 0, digit
;
91 for (i
= 0; i
< count
; i
++) {
92 CFStringRef oid_octet
= CFArrayGetValueAtIndex(oid
, i
);
93 SInt32 oid_octet_int_value
= CFStringGetIntValue(oid_octet
);
94 require(abs(oid_octet_int_value
) != INT32_MAX
, out
);
96 first_digit
= oid_octet_int_value
;
99 digit
= 40 * first_digit
+ oid_octet_int_value
;
101 digit
= oid_octet_int_value
;
102 mod128_oid_encoding(oid_der_data
, digit
, true);
108 CFReleaseSafe(oid_der_data
);
113 Get challenge password conversion and apply this:
115 ASCII ? => PrintableString subset: [A-Za-z0-9 '()+,-./:=?] ?
117 PrintableString > IA5String > UTF8String
119 Consider using IA5String for email address
122 static inline bool printable_string(CFStringRef string
)
126 CFCharacterSetRef printable_charset
=
127 CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
,
128 CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
129 "abcdefghijklmnopqrstuvwxyz"
130 "0123456789 '()+,-./:=?"));
131 CFCharacterSetRef not_printable_charset
=
132 CFCharacterSetCreateInvertedSet(kCFAllocatorDefault
, printable_charset
);
134 if (CFStringFindCharacterFromSet(string
, not_printable_charset
,
135 CFRangeMake(0, CFStringGetLength(string
)), 0, &found
))
138 CFReleaseSafe(printable_charset
);
139 CFReleaseSafe(not_printable_charset
);
144 static bool make_nss_atv(PRArenaPool
*poolp
,
145 const void * oid
, const void * value
, const unsigned char type_in
, NSS_ATV
*nss_atv
)
149 unsigned char type
= type_in
;
150 if (CFGetTypeID(value
) == CFStringGetTypeID()) {
151 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
152 kCFStringEncodingUTF8
);
153 buffer
= PORT_ArenaAlloc(poolp
, length
);
154 /* TODO: Switch to using CFStringGetBytes,since this code will do the wrong thing for embedded 0's */
155 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingASCII
)) {
156 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
158 if (type
&& type
!= SecASN1UTF8String
)
160 type
= SecASN1UTF8String
;
163 if (!type
|| type
== SecASN1PrintableString
) {
164 if (!printable_string(value
))
165 type
= SEC_ASN1_IA5_STRING
;
167 type
= SEC_ASN1_PRINTABLE_STRING
;
170 length
= strlen(buffer
);
172 else if (CFGetTypeID(value
) == CFDataGetTypeID()) {
173 /* will remain valid for the duration of the operation, still maybe copy into pool */
174 length
= CFDataGetLength(value
);
175 buffer
= (char *)CFDataGetBytePtr(value
);
177 size_t oid_length
= 0;
178 uint8_t *oid_data
= NULL
;
179 if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
180 if (CFEqual(kSecOidCommonName
, oid
)) {
181 oid_length
= oidCommonName
.length
; oid_data
= oidCommonName
.data
;
182 } else if (CFEqual(kSecOidCountryName
, oid
)) {
183 oid_length
= oidCountryName
.length
; oid_data
= oidCountryName
.data
;
184 } else if (CFEqual(kSecOidStateProvinceName
, oid
)) {
185 oid_length
= oidStateOrProvinceName
.length
; oid_data
= oidStateOrProvinceName
.data
;
186 } else if (CFEqual(kSecOidLocalityName
, oid
)) {
187 oid_length
= oidLocalityName
.length
; oid_data
= oidLocalityName
.data
;
188 } else if (CFEqual(kSecOidOrganization
, oid
)) {
189 oid_length
= oidOrganizationName
.length
; oid_data
= oidOrganizationName
.data
;
190 } else if (CFEqual(kSecOidOrganizationalUnit
, oid
)) {
191 oid_length
= oidOrganizationalUnitName
.length
; oid_data
= oidOrganizationalUnitName
.data
;
193 CFDataRef oid_der_data
= oid_der(oid
);
194 require(oid_der_data
, out
);
195 oid_length
= CFDataGetLength(oid_der_data
);
196 oid_data
= PORT_ArenaAlloc(poolp
, oid_length
);
197 require(oid_length
&& oid_data
, out
);
198 memcpy(oid_data
, CFDataGetBytePtr(oid_der_data
), oid_length
);
199 CFReleaseSafe(oid_der_data
);
201 } else if (CFGetTypeID(oid
) == CFDataGetTypeID()) {
202 /* will remain valid for the duration of the operation, still maybe copy into pool */
203 oid_length
= CFDataGetLength(oid
);
204 oid_data
= (uint8_t *)CFDataGetBytePtr(oid
);
206 NSS_ATV stage_nss_atv
= { { oid_length
, oid_data
},
207 { { length
, (uint8_t*)buffer
}, type
} };
208 *nss_atv
= stage_nss_atv
;
214 static NSS_RDN
**make_subject(PRArenaPool
*poolp
, CFArrayRef subject
)
218 CFIndex rdn_ix
, rdn_count
= CFArrayGetCount(subject
);
219 NSS_RDN
**rdnps
= PORT_ArenaZNewArray(poolp
, NSS_RDN
*, rdn_count
+ 1);
220 NSS_RDN
*rdns
= PORT_ArenaZNewArray(poolp
, NSS_RDN
, rdn_count
);
221 for (rdn_ix
= 0; rdn_ix
< rdn_count
; rdn_ix
++) {
222 rdnps
[rdn_ix
] = &rdns
[rdn_ix
];
223 CFArrayRef rdn
= CFArrayGetValueAtIndex(subject
, rdn_ix
);
224 CFIndex atv_ix
, atv_count
= CFArrayGetCount(rdn
);
225 rdns
[rdn_ix
].atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
*, atv_count
+ 1);
226 NSS_ATV
*atvs
= PORT_ArenaZNewArray(poolp
, NSS_ATV
, atv_count
);
227 for (atv_ix
= 0; atv_ix
< atv_count
; atv_ix
++) {
228 rdns
[rdn_ix
].atvs
[atv_ix
] = &atvs
[atv_ix
];
229 CFArrayRef atv
= CFArrayGetValueAtIndex(rdn
, atv_ix
);
230 if ((CFArrayGetCount(atv
) != 2)
231 || !make_nss_atv(poolp
, CFArrayGetValueAtIndex(atv
, 0),
232 CFArrayGetValueAtIndex(atv
, 1), 0, &atvs
[atv_ix
]))
239 struct make_general_names_context
{
246 static void make_general_names(const void *key
, const void *value
, void *context
)
248 struct make_general_names_context
*gn
= (struct make_general_names_context
*)context
;
250 CFArrayRef gn_values
= NULL
;
251 CFStringRef gn_value
= NULL
;
252 CFIndex entry_ix
, entry_count
= 0;
253 if (CFGetTypeID(value
) == CFArrayGetTypeID()) {
254 gn_values
= (CFArrayRef
)value
;
255 entry_count
= CFArrayGetCount(value
);
256 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
257 gn_value
= (CFStringRef
)value
;
261 require(entry_count
> 0, out
);
264 require(CFGetTypeID(key
) == CFStringGetTypeID(), out
);
266 if (!gn
->names
|| (gn
->count
== gn
->capacity
)) {
267 uint32_t capacity
= gn
->capacity
;
273 void * new_array
= PORT_ArenaZNewArray(gn
->poolp
, SecAsn1Item
, capacity
);
275 memcpy(new_array
, gn
->names
, gn
->capacity
);
276 gn
->names
= new_array
;
277 gn
->capacity
= capacity
;
280 NSS_GeneralName general_name_item
= { { }, -1 };
281 if (kCFCompareEqualTo
== CFStringCompare(CFSTR("dNSName"), key
, kCFCompareCaseInsensitive
))
282 general_name_item
.tag
= NGT_DNSName
;
283 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("rfc822Name"), key
, kCFCompareCaseInsensitive
))
284 general_name_item
.tag
= NGT_RFC822Name
;
285 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("uniformResourceIdentifier"), key
, kCFCompareCaseInsensitive
))
286 general_name_item
.tag
= NGT_URI
;
287 else if (kCFCompareEqualTo
== CFStringCompare(CFSTR("ntPrincipalName"), key
, kCFCompareCaseInsensitive
))
290 NT Principal in SubjectAltName is defined in the context of Smartcards:
292 http://www.oid-info.com/get/1.3.6.1.4.1.311.20.2.3
293 http://support.microsoft.com/default.aspx?scid=kb;en-us;281245
295 Subject Alternative Name = Other Name: Principal Name= (UPN). For example:
297 The UPN OtherName OID is : "1.3.6.1.4.1.311.20.2.3"
298 The UPN OtherName value: Must be ASN1-encoded UTF8 string
299 Subject = Distinguished name of user. This field is a mandatory extension, but the population of this field is optional.
302 /* OtherName ::= SEQUENCE {
303 type-id OBJECT IDENTIFIER,
304 value [0] EXPLICIT ANY DEFINED BY type-id
306 uint8_t nt_principal_oid
[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03 };
310 } nt_principal_other_name
;
311 nt_principal_other_name name
= {};
313 const SecAsn1Template my_other_name_template
[] = {
315 0, NULL
, sizeof(nt_principal_other_name
) },
316 { SEC_ASN1_OBJECT_ID
,
317 offsetof(nt_principal_other_name
,typeId
), },
318 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_EXPLICIT
| 0, offsetof(nt_principal_other_name
,value
), kSecAsn1UTF8StringTemplate
, },
321 const SecAsn1Template my_other_name_template_cons
[] = {
322 { SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_CONSTRUCTED
| NGT_OtherName
,
323 0, my_other_name_template
, sizeof(nt_principal_other_name
) }
329 require(gn_value
, out
);
330 require(CFGetTypeID(gn_value
) == CFStringGetTypeID(), out
);
331 length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
),
332 kCFStringEncodingUTF8
);
333 buffer
= PORT_ArenaAlloc(gn
->poolp
, length
);
334 if (!CFStringGetCString(value
, buffer
, length
, kCFStringEncodingUTF8
))
337 name
.typeId
.Length
= sizeof(nt_principal_oid
);
338 name
.typeId
.Data
= nt_principal_oid
;
339 name
.value
.Length
= strlen(buffer
);
340 name
.value
.Data
= (uint8_t*)buffer
;
341 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &name
, my_other_name_template_cons
);
344 /* We already encoded the value for the general name */
351 for (entry_ix
= 0; entry_ix
< entry_count
; entry_ix
++) {
352 CFTypeRef entry_value
= CFArrayGetValueAtIndex(gn_values
, entry_ix
);
353 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
)entry_value
),
354 kCFStringEncodingUTF8
); /* we only allow ASCII => only expect IA5Strings */
355 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
356 require(CFStringGetCString((CFStringRef
)entry_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
357 general_name_item
.item
.Data
= (uint8_t*)buffer
;
358 general_name_item
.item
.Length
= strlen(buffer
);
359 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
362 } else if (gn_value
) {
363 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(gn_value
),
364 kCFStringEncodingUTF8
);
365 char *buffer
= (char *)PORT_ArenaZNewArray(gn
->poolp
, uint8_t, buffer_size
);
366 require(CFStringGetCString(gn_value
, buffer
, buffer_size
, kCFStringEncodingASCII
), out
);
367 general_name_item
.item
.Data
= (uint8_t*)buffer
;
368 general_name_item
.item
.Length
= strlen(buffer
);
369 SEC_ASN1EncodeItem(gn
->poolp
, &gn
->names
[gn
->count
], &general_name_item
, kSecAsn1GeneralNameTemplate
);
376 static SecAsn1Item
make_subjectAltName_extension(PRArenaPool
*poolp
, CFDictionaryRef subjectAltNames
)
378 SecAsn1Item subjectAltExt
= {};
380 struct make_general_names_context context
= { poolp
, NULL
, 0 };
381 CFDictionaryApplyFunction(subjectAltNames
, make_general_names
, &context
);
383 // all general names in a sequence:
385 SecAsn1Item
**general_names
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, context
.count
+ 1);
386 for (ix
= 0; ix
< context
.count
; ix
++)
387 general_names
[ix
] = &context
.names
[ix
];
388 NSS_GeneralNames gnames
= { general_names
};
389 SEC_ASN1EncodeItem(poolp
, &subjectAltExt
, &gnames
, kSecAsn1GeneralNamesTemplate
);
391 return subjectAltExt
;
394 CFTypeRef kSecCSRChallengePassword
= CFSTR("csrChallengePassword");
395 CFTypeRef kSecSubjectAltName
= CFSTR("subjectAltName");
396 CFTypeRef kSecCertificateKeyUsage
= CFSTR("keyUsage");
397 CFTypeRef kSecCSRBasicContraintsPathLen
= CFSTR("basicConstraints");
399 static const uint8_t pkcs9ExtensionsRequested
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 14 };
400 static const uint8_t pkcs9ChallengePassword
[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 7 };
402 static const uint8_t encoded_asn1_true
= 0xFF;
403 static const SecAsn1Item asn1_true
=
404 { sizeof(encoded_asn1_true
), (uint8_t*)&encoded_asn1_true
};
406 static inline uint32_t highest_bit(uint32_t n
)
408 return ((n
) >> 16 ? ((n
)>>=16, 16) : 0) + \
409 ((n
) >> 8 ? ((n
)>>=8, 8) : 0) + \
410 ((n
) >> 4 ? ((n
)>>=4, 4) : 0) + \
411 ((n
) >> 2 ? ((n
)>>=2, 2) : 0) + \
412 ((n
) >> 1 ? ((n
)>>=1, 1) : 0) + \
418 extensions_from_parameters(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
420 uint32_t num_extensions
= 0, max_extensions
= 4;
421 NSS_CertExtension
**csr_extensions
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
*, max_extensions
);
422 NSS_CertExtension
*csr_extension
= PORT_ArenaZNewArray(poolp
, NSS_CertExtension
, max_extensions
);
424 CFNumberRef basic_contraints_num
= CFDictionaryGetValue(parameters
, kSecCSRBasicContraintsPathLen
);
425 if (basic_contraints_num
) {
426 NSS_BasicConstraints basic_contraints
= { asn1_true
, {} };
429 int basic_contraints_path_len
= 0;
430 require(CFNumberGetValue(basic_contraints_num
, kCFNumberIntType
, &basic_contraints_path_len
), out
);
431 if (basic_contraints_path_len
>= 0 && basic_contraints_path_len
< 256) {
432 path_len
= basic_contraints_path_len
;
433 basic_contraints
.pathLenConstraint
.Length
= sizeof(path_len
);
434 basic_contraints
.pathLenConstraint
.Data
= &path_len
;
437 csr_extension
[num_extensions
].extnId
.Data
= oidBasicConstraints
.data
;
438 csr_extension
[num_extensions
].extnId
.Length
= oidBasicConstraints
.length
;
439 csr_extension
[num_extensions
].critical
= asn1_true
;
441 SEC_ASN1EncodeItem(poolp
, &csr_extension
[num_extensions
].value
, &basic_contraints
,
442 kSecAsn1BasicConstraintsTemplate
);
443 require(num_extensions
++ < max_extensions
, out
);
446 CFDictionaryRef subject_alternate_names
= CFDictionaryGetValue(parameters
, kSecSubjectAltName
);
447 if (subject_alternate_names
) {
448 require(CFGetTypeID(subject_alternate_names
) == CFDictionaryGetTypeID(), out
);
449 csr_extension
[num_extensions
].value
= make_subjectAltName_extension(poolp
, subject_alternate_names
);
450 /* set up subjectAltName cert request value */
451 csr_extension
[num_extensions
].extnId
.Length
= oidSubjectAltName
.length
;
452 csr_extension
[num_extensions
].extnId
.Data
= oidSubjectAltName
.data
;
453 require(num_extensions
++ < max_extensions
, out
);
456 CFNumberRef key_usage_requested
= CFDictionaryGetValue(parameters
, kSecCertificateKeyUsage
);
457 SecAsn1Item key_usage_asn1_value
= { 0 };
458 if (key_usage_requested
) {
460 require(CFNumberGetValue(key_usage_requested
, kCFNumberIntType
, &key_usage_value
), out
);
461 if (key_usage_value
> 0) {
462 uint32_t key_usage_value_be
= 0, key_usage_mask
= 1<<31;
463 uint32_t key_usage_value_max_bitlen
= 9, key_usage_value_bitlen
= 0;
464 while(key_usage_value_max_bitlen
) {
465 if (key_usage_value
& 1) {
466 key_usage_value_be
|= key_usage_mask
;
467 key_usage_value_bitlen
= 10 - key_usage_value_max_bitlen
;
469 key_usage_value
>>= 1;
470 key_usage_value_max_bitlen
--;
471 key_usage_mask
>>= 1;
474 SecAsn1Item key_usage_input
= { key_usage_value_bitlen
,
475 ((uint8_t*)&key_usage_value_be
) + 3 - (key_usage_value_bitlen
>> 3) };
476 SEC_ASN1EncodeItem(poolp
, &key_usage_asn1_value
, &key_usage_input
, kSecAsn1BitStringTemplate
);
478 csr_extension
[num_extensions
].extnId
.Data
= oidKeyUsage
.data
;
479 csr_extension
[num_extensions
].extnId
.Length
= oidKeyUsage
.length
;
480 csr_extension
[num_extensions
].critical
= asn1_true
;
481 csr_extension
[num_extensions
].value
= key_usage_asn1_value
;
482 require(num_extensions
++ < max_extensions
, out
);
486 /* extensions requested (subjectAltName, keyUsage) sequence of extension sequences */
488 for (ix
= 0; ix
<= num_extensions
; ix
++)
489 csr_extensions
[ix
] = csr_extension
[ix
].extnId
.Length
? &csr_extension
[ix
] : NULL
;
492 return csr_extensions
;
496 NSS_Attribute
**nss_attributes_from_parameters_dict(PRArenaPool
*poolp
, CFDictionaryRef parameters
)
498 /* A challenge-password attribute must have a single attribute value.
500 ChallengePassword attribute values generated in accordance with this
501 version of this document SHOULD use the PrintableString encoding
502 whenever possible. If internationalization issues make this
503 impossible, the UTF8String alternative SHOULD be used. PKCS #9-
504 attribute processing systems MUST be able to recognize and process
505 all string types in DirectoryString values.
507 Upperbound of 255 defined for all PKCS#9 attributes.
509 pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840)
510 rsadsi(113549) pkcs(1) 9}
511 pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7}
516 uint32_t num_attrs
= 0;
518 CFStringRef challenge
= CFDictionaryGetValue(parameters
, kSecCSRChallengePassword
);
519 NSS_Attribute challenge_password_attr
= {};
521 CFIndex buffer_size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(challenge
),
522 kCFStringEncodingUTF8
); /* we only allow UTF8 or ASCII */
523 char *buffer
= (char *)PORT_ArenaZNewArray(poolp
, uint8_t, buffer_size
);
525 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingASCII
)) {
526 if (!CFStringGetCString(challenge
, buffer
, buffer_size
, kCFStringEncodingUTF8
))
530 if (!printable_string(challenge
))
533 SecAsn1Item
*challenge_password_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
534 SecAsn1Item challenge_password_raw
= { strlen(buffer
), (uint8_t*)buffer
};
535 SEC_ASN1EncodeItem(poolp
, challenge_password_value
, &challenge_password_raw
,
536 utf8
? kSecAsn1UTF8StringTemplate
: kSecAsn1PrintableStringTemplate
);
537 SecAsn1Item
**challenge_password_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
538 challenge_password_values
[0] = challenge_password_value
;
539 challenge_password_attr
.attrType
.Length
= sizeof(pkcs9ChallengePassword
);
540 challenge_password_attr
.attrType
.Data
= (uint8_t*)&pkcs9ChallengePassword
;
541 challenge_password_attr
.attrValue
= challenge_password_values
;
545 NSS_CertExtension
**extensions
= extensions_from_parameters(poolp
, parameters
);
546 NSS_Attribute extensions_requested_attr
= {};
548 SecAsn1Item
*extensions_requested_value
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
, 1);
549 SEC_ASN1EncodeItem(poolp
, extensions_requested_value
, &extensions
, kSecAsn1SequenceOfCertExtensionTemplate
);
550 SecAsn1Item
**extensions_requested_values
= PORT_ArenaZNewArray(poolp
, SecAsn1Item
*, 2);
551 extensions_requested_values
[0] = extensions_requested_value
;
552 extensions_requested_values
[1] = NULL
;
553 extensions_requested_attr
.attrType
.Length
= sizeof(pkcs9ExtensionsRequested
);
554 extensions_requested_attr
.attrType
.Data
= (uint8_t*)pkcs9ExtensionsRequested
;
555 extensions_requested_attr
.attrValue
= extensions_requested_values
;
559 NSS_Attribute
**attributes_ptr
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
*, num_attrs
+ 1);
560 NSS_Attribute
*attributes
= PORT_ArenaZNewArray(poolp
, NSS_Attribute
, num_attrs
);
561 if (challenge_password_attr
.attrType
.Length
) {
563 attributes
[num_attrs
] = challenge_password_attr
;
564 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
566 if (extensions_requested_attr
.attrType
.Length
) {
568 attributes
[num_attrs
] = extensions_requested_attr
;
569 attributes_ptr
[num_attrs
] = &attributes
[num_attrs
];
571 return attributes_ptr
;
578 static const uint8_t encoded_null
[2] = { SEC_ASN1_NULL
, 0 };
579 static const SecAsn1Item asn1_null
= { sizeof(encoded_null
), (uint8_t*)encoded_null
};
581 CFDataRef
SecGenerateCertificateRequestWithParameters(SecRDN
*subject
,
582 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
584 CFDataRef csr
= NULL
;
585 PRArenaPool
*poolp
= PORT_NewArena(1024);
586 CFDictionaryRef pubkey_attrs
= NULL
;
591 NSSCertRequest certReq
;
592 memset(&certReq
, 0, sizeof(certReq
));
595 unsigned char version
= 0;
596 certReq
.reqInfo
.version
.Length
= sizeof(version
);
597 certReq
.reqInfo
.version
.Data
= &version
;
600 unsigned atv_num
= 0, num
= 0;
603 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
604 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++)
606 atv_num
++; /* one more */
609 NSS_ATV atvs
[atv_num
];
610 NSS_ATV
*atvps
[atv_num
];
612 NSS_RDN
*rdnps
[num
+1];
614 unsigned rdn_num
= 0;
615 for (one_rdn
= subject
; *one_rdn
; one_rdn
++) {
616 rdns
[rdn_num
].atvs
= &atvps
[atv_num
];
617 rdnps
[rdn_num
] = &rdns
[rdn_num
];
619 for (one_atv
= *one_rdn
; one_atv
->oid
; one_atv
++) {
620 if (!make_nss_atv(poolp
, one_atv
->oid
, one_atv
->value
,
621 one_atv
->type
, &atvs
[atv_num
]))
623 atvps
[atv_num
] = &atvs
[atv_num
];
626 atvps
[atv_num
++] = NULL
;
628 rdnps
[rdn_num
] = NULL
;
629 certReq
.reqInfo
.subject
.rdns
= rdnps
;
631 /* public key info */
632 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
633 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
634 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
636 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
637 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
638 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
639 size_t signature_length
= sizeof(signature
);
641 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
642 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
644 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
645 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
647 /* encode request info by itself to calculate signature */
648 SecAsn1Item reqinfo
= {};
649 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
650 require(reqinfo
.Length
<=UINT32_MAX
, out
);
652 /* calculate signature */
653 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
654 CC_SHA1(reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
655 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
656 reqinfo_hash
, sizeof(reqinfo_hash
), signature
, &signature_length
), out
);
658 /* signature and info */
659 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha1Rsa
.length
;
660 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha1Rsa
.data
;
661 certReq
.signatureAlgorithm
.parameters
= asn1_null
;
662 certReq
.signature
.Data
= signature
;
663 certReq
.signature
.Length
= signature_length
* 8;
666 SecAsn1Item cert_request
= {};
667 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
668 kSecAsn1CertRequestTemplate
), out
);
669 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
673 PORT_FreeArena(poolp
, PR_TRUE
);
674 CFReleaseSafe(pubkey_attrs
);
678 CFDataRef
SecGenerateCertificateRequest(CFArrayRef subject
,
679 CFDictionaryRef parameters
, SecKeyRef publicKey
, SecKeyRef privateKey
)
681 CFDataRef csr
= NULL
;
682 PRArenaPool
*poolp
= PORT_NewArena(1024);
683 CFDictionaryRef pubkey_attrs
= NULL
;
688 NSSCertRequest certReq
;
689 memset(&certReq
, 0, sizeof(certReq
));
692 unsigned char version
= 0;
693 certReq
.reqInfo
.version
.Length
= sizeof(version
);
694 certReq
.reqInfo
.version
.Data
= &version
;
697 certReq
.reqInfo
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
699 /* public key info */
700 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Length
= oidRsa
.length
;
701 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.algorithm
.Data
= oidRsa
.data
;
702 certReq
.reqInfo
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
704 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
705 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
706 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
707 size_t signature_length
= sizeof(signature
);
709 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
710 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
712 certReq
.reqInfo
.attributes
= nss_attributes_from_parameters_dict(poolp
, parameters
);
713 SecCmsArraySortByDER((void **)certReq
.reqInfo
.attributes
, kSecAsn1AttributeTemplate
, NULL
);
715 /* encode request info by itself to calculate signature */
716 SecAsn1Item reqinfo
= {};
717 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
718 require(reqinfo
.Length
<=UINT32_MAX
, out
);
720 /* calculate signature */
721 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
722 CC_SHA1(reqinfo
.Data
, reqinfo
.Length
, reqinfo_hash
);
723 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
724 reqinfo_hash
, sizeof(reqinfo_hash
), signature
, &signature_length
), out
);
726 /* signature and info */
727 certReq
.signatureAlgorithm
.algorithm
.Length
= oidSha1Rsa
.length
;
728 certReq
.signatureAlgorithm
.algorithm
.Data
= oidSha1Rsa
.data
;
729 certReq
.signatureAlgorithm
.parameters
= asn1_null
;
730 certReq
.signature
.Data
= signature
;
731 certReq
.signature
.Length
= signature_length
* 8;
734 SecAsn1Item cert_request
= {};
735 require_quiet(SEC_ASN1EncodeItem(poolp
, &cert_request
, &certReq
,
736 kSecAsn1CertRequestTemplate
), out
);
737 csr
= CFDataCreate(kCFAllocatorDefault
, cert_request
.Data
, cert_request
.Length
);
741 PORT_FreeArena(poolp
, PR_TRUE
);
742 CFReleaseSafe(pubkey_attrs
);
746 bool SecVerifyCertificateRequest(CFDataRef csr
, SecKeyRef
*publicKey
,
747 CFStringRef
*challenge
, CFDataRef
*subject
, CFDataRef
*extensions
)
749 PRArenaPool
*poolp
= PORT_NewArena(1024);
750 SecKeyRef candidatePublicKey
= NULL
;
752 NSSCertRequest certReq
;
753 memset(&certReq
, 0, sizeof(certReq
));
754 SecAsn1Item csr_item
= { CFDataGetLength(csr
), (uint8_t*)CFDataGetBytePtr(csr
) };
755 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &certReq
, kSecAsn1CertRequestTemplate
,
758 /* signature and info */
759 require(certReq
.signatureAlgorithm
.algorithm
.Length
== oidSha1Rsa
.length
, out
);
760 require_noerr(memcmp(oidSha1Rsa
.data
, certReq
.signatureAlgorithm
.algorithm
.Data
,
761 oidSha1Rsa
.length
), out
);
762 require(certReq
.signatureAlgorithm
.parameters
.Length
== asn1_null
.Length
, out
);
763 require_noerr(memcmp(asn1_null
.Data
, certReq
.signatureAlgorithm
.parameters
.Data
,
764 asn1_null
.Length
), out
);
766 /* encode request info by itself to calculate signature */
767 SecAsn1Item reqinfo
= {};
768 SEC_ASN1EncodeItem(poolp
, &reqinfo
, &certReq
.reqInfo
, kSecAsn1CertRequestInfoTemplate
);
770 /* calculate signature */
771 uint8_t reqinfo_hash
[CC_SHA1_DIGEST_LENGTH
];
772 require(reqinfo
.Length
<=UINT32_MAX
, out
);
773 CC_SHA1(reqinfo
.Data
, (CC_LONG
)reqinfo
.Length
, reqinfo_hash
);
775 /* @@@ check for version 0 */
777 require(candidatePublicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
778 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Data
,
779 certReq
.reqInfo
.subjectPublicKeyInfo
.subjectPublicKey
.Length
/ 8,
780 kSecKeyEncodingPkcs1
), out
);
782 require_noerr_quiet(SecKeyRawVerify(candidatePublicKey
, kSecPaddingPKCS1SHA1
,
783 reqinfo_hash
, sizeof(reqinfo_hash
),
784 certReq
.signature
.Data
, certReq
.signature
.Length
/ 8), out
);
786 SecAsn1Item subject_item
= { 0 }, extensions_item
= { 0 }, challenge_item
= { 0 };
787 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
,
788 &certReq
.reqInfo
.subject
, kSecAsn1NameTemplate
), out
);
790 if (*certReq
.reqInfo
.attributes
) {
792 for (ix
= 0; certReq
.reqInfo
.attributes
[ix
]; ix
++) {
793 NSS_Attribute
*attr
= certReq
.reqInfo
.attributes
[ix
];
794 if ( (sizeof(pkcs9ChallengePassword
) == attr
->attrType
.Length
) &&
795 !memcmp(pkcs9ChallengePassword
, attr
->attrType
.Data
, sizeof(pkcs9ChallengePassword
)))
796 challenge_item
= *attr
->attrValue
[0];
797 else if ( (sizeof(pkcs9ExtensionsRequested
) == attr
->attrType
.Length
) &&
798 !memcmp(pkcs9ExtensionsRequested
, attr
->attrType
.Data
, sizeof(pkcs9ExtensionsRequested
)))
799 extensions_item
= *attr
->attrValue
[0];
803 if (subject
&& subject_item
.Length
)
804 *subject
= CFDataCreate(kCFAllocatorDefault
, subject_item
.Data
, subject_item
.Length
);
805 if (extensions
&& extensions_item
.Length
)
806 *extensions
= CFDataCreate(kCFAllocatorDefault
, extensions_item
.Data
, extensions_item
.Length
);
807 if (challenge
&& challenge_item
.Length
) {
808 SecAsn1Item string
= { 0 };
809 SECStatus rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1UTF8StringTemplate
, &challenge_item
);
811 rv
= SEC_ASN1DecodeItem(poolp
, &string
, kSecAsn1PrintableStringTemplate
, &challenge_item
);
813 *challenge
= CFStringCreateWithBytes(kCFAllocatorDefault
, string
.Data
, string
.Length
, kCFStringEncodingUTF8
, false);
818 *publicKey
= candidatePublicKey
;
819 candidatePublicKey
= NULL
;
823 CFReleaseSafe(candidatePublicKey
);
825 PORT_FreeArena(poolp
, PR_TRUE
);
829 #define HIDIGIT(v) (((v) / 10) + '0')
830 #define LODIGIT(v) (((v) % 10) + '0')
833 DER_CFDateToUTCTime(PRArenaPool
*poolp
, CFAbsoluteTime date
, SecAsn1Item
* utcTime
)
835 CFGregorianDate gdate
= CFAbsoluteTimeGetGregorianDate(date
, NULL
/* GMT */);
839 utcTime
->Length
= 13;
840 utcTime
->Data
= d
= PORT_ArenaAlloc(poolp
, 13);
844 /* UTC time does not handle the years before 1950 */
845 if (gdate
.year
< 1950)
848 /* remove the century since it's added to the year by the
849 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
851 second
= gdate
.second
;
853 d
[0] = HIDIGIT(gdate
.year
);
854 d
[1] = LODIGIT(gdate
.year
);
855 d
[2] = HIDIGIT(gdate
.month
);
856 d
[3] = LODIGIT(gdate
.month
);
857 d
[4] = HIDIGIT(gdate
.day
);
858 d
[5] = LODIGIT(gdate
.day
);
859 d
[6] = HIDIGIT(gdate
.hour
);
860 d
[7] = LODIGIT(gdate
.hour
);
861 d
[8] = HIDIGIT(gdate
.minute
);
862 d
[9] = LODIGIT(gdate
.minute
);
863 d
[10] = HIDIGIT(second
);
864 d
[11] = LODIGIT(second
);
870 SecGenerateSelfSignedCertificate(CFArrayRef subject
, CFDictionaryRef parameters
,
871 SecKeyRef publicKey
, SecKeyRef privateKey
)
873 SecCertificateRef cert
= NULL
;
874 PRArenaPool
*poolp
= PORT_NewArena(1024);
875 CFDictionaryRef pubkey_attrs
= NULL
;
879 NSS_Certificate cert_tmpl
;
880 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
883 unsigned char version
= 2;
884 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
885 cert_tmpl
.tbs
.version
.Data
= &version
;
888 unsigned char serialNumber
= 1;
889 cert_tmpl
.tbs
.serialNumber
.Length
= sizeof(serialNumber
);
890 cert_tmpl
.tbs
.serialNumber
.Data
= &serialNumber
;
893 cert_tmpl
.tbs
.issuer
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
894 cert_tmpl
.tbs
.subject
.rdns
= cert_tmpl
.tbs
.issuer
.rdns
;
896 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
897 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
898 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
899 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
902 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, parameters
);
904 /* @@@ we only handle rsa keys */
905 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
906 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
907 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
908 /* public key data and algorithm */
909 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
910 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
912 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
913 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
914 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
916 /* signature algorithm */
917 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
918 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
919 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
920 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
922 /* encode request info by itself to calculate signature */
923 SecAsn1Item tbscert
= {};
924 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
926 /* calculate signature */
927 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
928 require(tbscert
.Length
<=UINT32_MAX
, out
);
929 CC_SHA1(tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
930 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
931 size_t signature_length
= sizeof(signature
);
932 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
933 tbscert_hash
, sizeof(tbscert_hash
), signature
, &signature_length
), out
);
936 cert_tmpl
.signature
.Data
= signature
;
937 cert_tmpl
.signature
.Length
= signature_length
* 8;
940 SecAsn1Item signed_cert
= {};
941 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
942 kSecAsn1SignedCertTemplate
), out
);
943 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
944 signed_cert
.Data
, signed_cert
.Length
);
948 PORT_FreeArena(poolp
, PR_TRUE
);
949 CFReleaseSafe(pubkey_attrs
);
955 SecIdentitySignCertificate(SecIdentityRef issuer
, CFDataRef serialno
,
956 SecKeyRef publicKey
, CFTypeRef subject
, CFTypeRef extensions
)
958 SecCertificateRef cert
= NULL
;
959 SecKeyRef privateKey
= NULL
;
961 PRArenaPool
*poolp
= PORT_NewArena(1024);
962 CFDictionaryRef pubkey_attrs
= NULL
;
966 NSS_Certificate cert_tmpl
;
967 memset(&cert_tmpl
, 0, sizeof(cert_tmpl
));
970 unsigned char version
= 2;
971 cert_tmpl
.tbs
.version
.Length
= sizeof(version
);
972 cert_tmpl
.tbs
.version
.Data
= &version
;
975 cert_tmpl
.tbs
.serialNumber
.Length
= CFDataGetLength(serialno
);
976 cert_tmpl
.tbs
.serialNumber
.Data
= (uint8_t*)CFDataGetBytePtr(serialno
);
979 if (CFArrayGetTypeID() == CFGetTypeID(subject
))
980 cert_tmpl
.tbs
.subject
.rdns
= make_subject(poolp
, (CFArrayRef
)subject
);
981 else if (CFDataGetTypeID() == CFGetTypeID(subject
)) {
982 SecAsn1Item subject_item
= { CFDataGetLength(subject
), (uint8_t*)CFDataGetBytePtr(subject
) };
983 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.subject
.rdns
, kSecAsn1NameTemplate
, &subject_item
), out
);
987 SecCertificateRef issuer_cert
= NULL
;
988 require_noerr(SecIdentityCopyCertificate(issuer
, &issuer_cert
), out
);
989 CFDataRef issuer_name
= SecCertificateCopyIssuerSequence(issuer_cert
);
990 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_name
), (uint8_t*)CFDataGetBytePtr(issuer_name
) };
991 require_noerr_action_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.issuer
.rdns
,
992 kSecAsn1NameTemplate
, &issuer_item
), out
, CFReleaseNull(issuer_name
));
993 CFReleaseNull(issuer_name
);
995 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent(), &cert_tmpl
.tbs
.validity
.notBefore
.item
);
996 cert_tmpl
.tbs
.validity
.notBefore
.tag
= SEC_ASN1_UTC_TIME
;
997 DER_CFDateToUTCTime(poolp
, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl
.tbs
.validity
.notAfter
.item
);
998 cert_tmpl
.tbs
.validity
.notAfter
.tag
= SEC_ASN1_UTC_TIME
;
1002 if (CFDataGetTypeID() == CFGetTypeID(extensions
)) {
1003 SecAsn1Item requested_extensions
= { CFDataGetLength(extensions
), (uint8_t*)CFDataGetBytePtr(extensions
) };
1004 //NSS_CertExtension **requested_extensions_decoded;
1005 require_noerr_quiet(SEC_ASN1DecodeItem(poolp
, &cert_tmpl
.tbs
.extensions
,
1006 kSecAsn1SequenceOfCertExtensionTemplate
, &requested_extensions
), out
);
1007 } else if (CFDictionaryGetTypeID() == CFGetTypeID(extensions
)) {
1008 cert_tmpl
.tbs
.extensions
= extensions_from_parameters(poolp
, extensions
);
1012 /* @@@ we only handle rsa keys */
1013 pubkey_attrs
= SecKeyCopyAttributeDictionary(publicKey
);
1014 CFTypeRef key_type
= CFDictionaryGetValue(pubkey_attrs
, kSecAttrKeyType
);
1015 if (key_type
&& CFEqual(key_type
, kSecAttrKeyTypeRSA
)) {
1016 /* public key data and algorithm */
1017 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.algorithm
= CSSMOID_RSA
;
1018 cert_tmpl
.tbs
.subjectPublicKeyInfo
.algorithm
.parameters
= asn1_null
;
1020 CFDataRef pkcs1_pubkey
= (CFDataRef
)CFDictionaryGetValue(pubkey_attrs
, kSecValueData
);
1021 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Length
= 8 * CFDataGetLength(pkcs1_pubkey
);
1022 cert_tmpl
.tbs
.subjectPublicKeyInfo
.subjectPublicKey
.Data
= (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey
);
1024 /* signature algorithm */
1025 cert_tmpl
.tbs
.signature
.algorithm
= CSSMOID_SHA1WithRSA
;
1026 cert_tmpl
.tbs
.signature
.parameters
= asn1_null
;
1027 cert_tmpl
.signatureAlgorithm
.algorithm
= CSSMOID_SHA1WithRSA
;
1028 cert_tmpl
.signatureAlgorithm
.parameters
= asn1_null
;
1030 /* encode request info by itself to calculate signature */
1031 SecAsn1Item tbscert
= {};
1032 SEC_ASN1EncodeItem(poolp
, &tbscert
, &cert_tmpl
.tbs
, kSecAsn1TBSCertificateTemplate
);
1034 /* calculate signature */
1035 uint8_t tbscert_hash
[CC_SHA1_DIGEST_LENGTH
];
1036 require(tbscert
.Length
<=UINT32_MAX
, out
);
1037 CC_SHA1(tbscert
.Data
, tbscert
.Length
, tbscert_hash
);
1038 uint8_t signature
[8 * CFDataGetLength(pkcs1_pubkey
)];
1039 size_t signature_length
= sizeof(signature
);
1041 require_noerr_quiet(SecIdentityCopyPrivateKey(issuer
, &privateKey
), out
);
1042 require_noerr_quiet(SecKeyRawSign(privateKey
, kSecPaddingPKCS1SHA1
,
1043 tbscert_hash
, sizeof(tbscert_hash
), signature
, &signature_length
), out
);
1046 cert_tmpl
.signature
.Data
= signature
;
1047 cert_tmpl
.signature
.Length
= signature_length
* 8;
1050 SecAsn1Item signed_cert
= {};
1051 require_quiet(SEC_ASN1EncodeItem(poolp
, &signed_cert
, &cert_tmpl
,
1052 kSecAsn1SignedCertTemplate
), out
);
1053 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
,
1054 signed_cert
.Data
, signed_cert
.Length
);
1057 CFReleaseSafe(privateKey
);
1059 PORT_FreeArena(poolp
, PR_TRUE
);
1060 CFReleaseSafe(pubkey_attrs
);
1065 SecGenerateCertificateRequestSubject(SecCertificateRef ca_certificate
, CFArrayRef subject
)
1067 CFMutableDataRef sequence
= NULL
;
1068 PRArenaPool
*poolp
= PORT_NewArena(1024);
1073 Going agains the spec here:
1075 3.2.3. GetCertInitial
1077 The messageData for this type consists of a DER-encoded
1078 IssuerAndSubject (Section 3.2.3.1). The issuer is set to the
1079 issuerName from the certification authority from which we are issued
1080 certificates. The Subject is set to the SubjectName we used when
1081 requesting the certificate.
1083 That clearly says use the issuer of the cert issuing certificate. Since
1084 it is combined with the subject of the to-be-issued certificate, that
1085 seems a mistake. If we take the subject of the issuer and the subject
1086 of the certificate we're interested in, we get the issuer and subject
1087 the certificate to be returned will have.
1090 CFDataRef issuer_sequence
= SecCertificateCopySubjectSequence(ca_certificate
);
1091 SecAsn1Item subject_item
= { 0 };
1092 SecAsn1Item issuer_item
= { CFDataGetLength(issuer_sequence
), (uint8_t*)CFDataGetBytePtr(issuer_sequence
) };
1093 NSS_Name nss_subject
= { make_subject(poolp
, subject
) };
1094 require_quiet(SEC_ASN1EncodeItem(poolp
, &subject_item
, &nss_subject
, kSecAsn1NameTemplate
), out
);
1096 DERSize sequence_length
= DERLengthOfLength(subject_item
.Length
+ issuer_item
.Length
);
1097 DERSize seq_len_length
= subject_item
.Length
+ issuer_item
.Length
+ 1 /* SEQUENCE */ +
1099 sequence
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
1100 CFDataSetLength(sequence
, seq_len_length
);
1101 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1102 *sequence_ptr
++ = 0x30; //ASN1_CONSTR_SEQUENCE;
1103 require_noerr_quiet(DEREncodeLength(subject_item
.Length
+ issuer_item
.Length
, sequence_ptr
, &sequence_length
), out
);
1104 sequence_ptr
+= sequence_length
;
1105 memcpy(sequence_ptr
, issuer_item
.Data
, issuer_item
.Length
);
1106 memcpy(sequence_ptr
+ issuer_item
.Length
, subject_item
.Data
, subject_item
.Length
);
1109 CFReleaseSafe(issuer_sequence
);
1111 PORT_FreeArena(poolp
, PR_TRUE
);