X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/sec/Security/SecCertificateRequest.c diff --git a/sec/Security/SecCertificateRequest.c b/sec/Security/SecCertificateRequest.c deleted file mode 100644 index 7f748f4f..00000000 --- a/sec/Security/SecCertificateRequest.c +++ /dev/null @@ -1,1171 +0,0 @@ -/* - * Copyright (c) 2008-2009 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) -// ENABLE_CMS 0 -OSStatus SecCmsArraySortByDER(void **objs, const SecAsn1Template *objtemplate, void **objs2); -#else -// ENABLE_CMS 1 -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "SecCertificateRequest.h" - -CFTypeRef kSecOidCommonName = CFSTR("CN"); -CFTypeRef kSecOidCountryName = CFSTR("C"); -CFTypeRef kSecOidStateProvinceName = CFSTR("ST"); -CFTypeRef kSecOidLocalityName = CFSTR("L"); -CFTypeRef kSecOidOrganization = CFSTR("O"); -CFTypeRef kSecOidOrganizationalUnit = CFSTR("OU"); -//CFTypeRef kSecOidEmailAddress = CFSTR("1.2.840.113549.1.9.1"); -// keep natural order: C > ST > L > O > OU > CN > Email - -const unsigned char SecASN1PrintableString = SEC_ASN1_PRINTABLE_STRING; -const unsigned char SecASN1UTF8String = SEC_ASN1_UTF8_STRING; - -static uint8_t * mod128_oid_encoding_ptr(uint8_t *ptr, uint32_t src, bool final) -{ - if (src > 128) - ptr = mod128_oid_encoding_ptr(ptr, src / 128, false); - - unsigned char octet = src % 128; - if (!final) - octet |= 128; - *ptr++ = octet; - - return ptr; -} - -static uint8_t * oid_der_data(PRArenaPool *poolp, CFStringRef oid_string, size_t *oid_data_len) -{ - /* estimate encoded length from base 10 (4 bits) to base 128 (7 bits) */ - size_t tmp_oid_length = ((CFStringGetLength(oid_string) * 4) / 7) + 1; - uint8_t *tmp_oid_data = PORT_ArenaAlloc(poolp, tmp_oid_length); - uint8_t *tmp_oid_data_ptr = tmp_oid_data; - - CFArrayRef oid = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, - oid_string, CFSTR(".")); - CFIndex i = 0, count = CFArrayGetCount(oid); - SInt32 first_digit = 0, digit; - for (i = 0; i < count; i++) { - CFStringRef oid_octet = CFArrayGetValueAtIndex(oid, i); - SInt32 oid_octet_int_value = CFStringGetIntValue(oid_octet); - require(abs(oid_octet_int_value) != INT32_MAX, out); - if (i == 0) - first_digit = oid_octet_int_value; - else { - if (i == 1) - digit = 40 * first_digit + oid_octet_int_value; - else - digit = oid_octet_int_value; - tmp_oid_data_ptr = mod128_oid_encoding_ptr(tmp_oid_data_ptr, digit, true); - } - } - CFReleaseSafe(oid); - - *oid_data_len = tmp_oid_data_ptr - tmp_oid_data; - return tmp_oid_data; -out: - return NULL; -} - - -/* -Get challenge password conversion and apply this: - -ASCII ? => PrintableString subset: [A-Za-z0-9 '()+,-./:=?] ? - -PrintableString > IA5String > UTF8String - -Consider using IA5String for email address -*/ - -static inline bool printable_string(CFStringRef string) -{ - bool result = true; - - CFCharacterSetRef printable_charset = - CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, - CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789 '()+,-./:=?")); - CFCharacterSetRef not_printable_charset = - CFCharacterSetCreateInvertedSet(kCFAllocatorDefault, printable_charset); - CFRange found; - if (CFStringFindCharacterFromSet(string, not_printable_charset, - CFRangeMake(0, CFStringGetLength(string)), 0, &found)) - result = false; - - CFReleaseSafe(printable_charset); - CFReleaseSafe(not_printable_charset); - - return result; -} - -static bool make_nss_atv(PRArenaPool *poolp, - const void * oid, const void * value, const unsigned char type_in, NSS_ATV *nss_atv) -{ - size_t length = 0; - char *buffer = NULL; - unsigned char type = type_in; - if (CFGetTypeID(value) == CFStringGetTypeID()) { - length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), - kCFStringEncodingUTF8); - buffer = PORT_ArenaAlloc(poolp, length); - /* TODO: Switch to using CFStringGetBytes,since this code will do the wrong thing for embedded 0's */ - if (!CFStringGetCString(value, buffer, length, kCFStringEncodingASCII)) { - if (!CFStringGetCString(value, buffer, length, kCFStringEncodingUTF8)) - return false; - if (type && type != SecASN1UTF8String) - return false; - type = SecASN1UTF8String; - } - else { - if (!type || type == SecASN1PrintableString) { - if (!printable_string(value)) - type = SEC_ASN1_IA5_STRING; - else - type = SEC_ASN1_PRINTABLE_STRING; - } - } - length = strlen(buffer); - } - else if (CFGetTypeID(value) == CFDataGetTypeID()) { - /* will remain valid for the duration of the operation, still maybe copy into pool */ - length = CFDataGetLength(value); - buffer = (char *)CFDataGetBytePtr(value); - } - size_t oid_length = 0; - uint8_t *oid_data = NULL; - if (CFGetTypeID(oid) == CFStringGetTypeID()) { - if (CFEqual(kSecOidCommonName, oid)) { - oid_length = oidCommonName.length; oid_data = oidCommonName.data; - } else if (CFEqual(kSecOidCountryName, oid)) { - oid_length = oidCountryName.length; oid_data = oidCountryName.data; - } else if (CFEqual(kSecOidStateProvinceName, oid)) { - oid_length = oidStateOrProvinceName.length; oid_data = oidStateOrProvinceName.data; - } else if (CFEqual(kSecOidLocalityName, oid)) { - oid_length = oidLocalityName.length; oid_data = oidLocalityName.data; - } else if (CFEqual(kSecOidOrganization, oid)) { - oid_length = oidOrganizationName.length; oid_data = oidOrganizationName.data; - } else if (CFEqual(kSecOidOrganizationalUnit, oid)) { - oid_length = oidOrganizationalUnitName.length; oid_data = oidOrganizationalUnitName.data; - } else { - oid_data = oid_der_data(poolp, oid, &oid_length); - require(oid_data, out); - } - } else if (CFGetTypeID(oid) == CFDataGetTypeID()) { - /* will remain valid for the duration of the operation, still maybe copy into pool */ - oid_length = CFDataGetLength(oid); - oid_data = (uint8_t *)CFDataGetBytePtr(oid); - } - NSS_ATV stage_nss_atv = { { oid_length, oid_data }, - { { length, (uint8_t*)buffer }, type } }; - *nss_atv = stage_nss_atv; - return true; -out: - return false; -} - -static NSS_RDN **make_subject(PRArenaPool *poolp, CFArrayRef subject) -{ - if (!subject) - return NULL; - CFIndex rdn_ix, rdn_count = CFArrayGetCount(subject); - NSS_RDN **rdnps = PORT_ArenaZNewArray(poolp, NSS_RDN *, rdn_count + 1); - NSS_RDN *rdns = PORT_ArenaZNewArray(poolp, NSS_RDN, rdn_count); - for (rdn_ix = 0; rdn_ix < rdn_count; rdn_ix++) { - rdnps[rdn_ix] = &rdns[rdn_ix]; - CFArrayRef rdn = CFArrayGetValueAtIndex(subject, rdn_ix); - CFIndex atv_ix, atv_count = CFArrayGetCount(rdn); - rdns[rdn_ix].atvs = PORT_ArenaZNewArray(poolp, NSS_ATV *, atv_count + 1); - NSS_ATV *atvs = PORT_ArenaZNewArray(poolp, NSS_ATV, atv_count); - for (atv_ix = 0; atv_ix < atv_count; atv_ix++) { - rdns[rdn_ix].atvs[atv_ix] = &atvs[atv_ix]; - CFArrayRef atv = CFArrayGetValueAtIndex(rdn, atv_ix); - if ((CFArrayGetCount(atv) != 2) - || !make_nss_atv(poolp, CFArrayGetValueAtIndex(atv, 0), - CFArrayGetValueAtIndex(atv, 1), 0, &atvs[atv_ix])) - return NULL; - } - } - return rdnps; -} - -struct make_general_names_context { - PRArenaPool *poolp; - SecAsn1Item *names; - uint32_t count; - uint32_t capacity; -}; - -static void make_general_names(const void *key, const void *value, void *context) -{ - struct make_general_names_context *gn = (struct make_general_names_context *)context; - require(value,out); - CFArrayRef gn_values = NULL; - CFStringRef gn_value = NULL; - CFIndex entry_ix, entry_count = 0; - if (CFGetTypeID(value) == CFArrayGetTypeID()) { - gn_values = (CFArrayRef)value; - entry_count = CFArrayGetCount(value); - } else if (CFGetTypeID(value) == CFStringGetTypeID()) { - gn_value = (CFStringRef)value; - entry_count = 1; - } - - require(entry_count > 0, out); - - require(key,out); - require(CFGetTypeID(key) == CFStringGetTypeID(), out); - - if (!gn->names || (gn->count == gn->capacity)) { - uint32_t capacity = gn->capacity; - if (capacity) - capacity *= 2; - else - capacity = 10; - - void * new_array = PORT_ArenaZNewArray(gn->poolp, SecAsn1Item, capacity); - if (gn->names) - memcpy(new_array, gn->names, gn->capacity); - gn->names = new_array; - gn->capacity = capacity; - } - - NSS_GeneralName general_name_item = { { }, -1 }; - if (kCFCompareEqualTo == CFStringCompare(CFSTR("dNSName"), key, kCFCompareCaseInsensitive)) - general_name_item.tag = NGT_DNSName; - else if (kCFCompareEqualTo == CFStringCompare(CFSTR("rfc822Name"), key, kCFCompareCaseInsensitive)) - general_name_item.tag = NGT_RFC822Name; - else if (kCFCompareEqualTo == CFStringCompare(CFSTR("uniformResourceIdentifier"), key, kCFCompareCaseInsensitive)) - general_name_item.tag = NGT_URI; - else if (kCFCompareEqualTo == CFStringCompare(CFSTR("ntPrincipalName"), key, kCFCompareCaseInsensitive)) - { - /* - NT Principal in SubjectAltName is defined in the context of Smartcards: - - http://www.oid-info.com/get/1.3.6.1.4.1.311.20.2.3 - http://support.microsoft.com/default.aspx?scid=kb;en-us;281245 - - Subject Alternative Name = Other Name: Principal Name= (UPN). For example: - UPN = user1@name.com - The UPN OtherName OID is : "1.3.6.1.4.1.311.20.2.3" - The UPN OtherName value: Must be ASN1-encoded UTF8 string - Subject = Distinguished name of user. This field is a mandatory extension, but the population of this field is optional. - */ - - /* OtherName ::= SEQUENCE { - type-id OBJECT IDENTIFIER, - value [0] EXPLICIT ANY DEFINED BY type-id - } */ - uint8_t nt_principal_oid[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03 }; - typedef struct { - SecAsn1Oid typeId; - SecAsn1Item value; - } nt_principal_other_name; - nt_principal_other_name name = {}; - - const SecAsn1Template my_other_name_template[] = { - { SEC_ASN1_SEQUENCE, - 0, NULL, sizeof(nt_principal_other_name) }, - { SEC_ASN1_OBJECT_ID, - offsetof(nt_principal_other_name,typeId), }, - { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | 0, offsetof(nt_principal_other_name,value), kSecAsn1UTF8StringTemplate, }, - { 0, } - }; - const SecAsn1Template my_other_name_template_cons[] = { - { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | NGT_OtherName, - 0, my_other_name_template, sizeof(nt_principal_other_name) } - }; - - size_t length = 0; - char *buffer = NULL; - - require(gn_value, out); - require(CFGetTypeID(gn_value) == CFStringGetTypeID(), out); - length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), - kCFStringEncodingUTF8); - buffer = PORT_ArenaAlloc(gn->poolp, length); - if (!CFStringGetCString(value, buffer, length, kCFStringEncodingUTF8)) - goto out; - - name.typeId.Length = sizeof(nt_principal_oid); - name.typeId.Data = nt_principal_oid; - name.value.Length = strlen(buffer); - name.value.Data = (uint8_t*)buffer; - SEC_ASN1EncodeItem(gn->poolp, &gn->names[gn->count], &name, my_other_name_template_cons); - gn->count++; - - /* We already encoded the value for the general name */ - goto out; - } - else - goto out; - - if (gn_values) { - for (entry_ix = 0; entry_ix < entry_count; entry_ix++) { - CFTypeRef entry_value = CFArrayGetValueAtIndex(gn_values, entry_ix); - CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef)entry_value), - kCFStringEncodingUTF8); /* we only allow ASCII => only expect IA5Strings */ - char *buffer = (char *)PORT_ArenaZNewArray(gn->poolp, uint8_t, buffer_size); - require(CFStringGetCString((CFStringRef)entry_value, buffer, buffer_size, kCFStringEncodingASCII), out); - general_name_item.item.Data = (uint8_t*)buffer; - general_name_item.item.Length = strlen(buffer); - SEC_ASN1EncodeItem(gn->poolp, &gn->names[gn->count], &general_name_item, kSecAsn1GeneralNameTemplate); - gn->count++; - } - } else if (gn_value) { - CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(gn_value), - kCFStringEncodingUTF8); - char *buffer = (char *)PORT_ArenaZNewArray(gn->poolp, uint8_t, buffer_size); - require(CFStringGetCString(gn_value, buffer, buffer_size, kCFStringEncodingASCII), out); - general_name_item.item.Data = (uint8_t*)buffer; - general_name_item.item.Length = strlen(buffer); - SEC_ASN1EncodeItem(gn->poolp, &gn->names[gn->count], &general_name_item, kSecAsn1GeneralNameTemplate); - gn->count++; - } -out: - return; -} - -static SecAsn1Item make_subjectAltName_extension(PRArenaPool *poolp, CFDictionaryRef subjectAltNames) -{ - SecAsn1Item subjectAltExt = {}; - - struct make_general_names_context context = { poolp, NULL, 0 }; - CFDictionaryApplyFunction(subjectAltNames, make_general_names, &context); - - // all general names in a sequence: - uint32_t ix; - SecAsn1Item **general_names = PORT_ArenaZNewArray(poolp, SecAsn1Item *, context.count + 1); - for (ix = 0; ix < context.count; ix++) - general_names[ix] = &context.names[ix]; - NSS_GeneralNames gnames = { general_names }; - SEC_ASN1EncodeItem(poolp, &subjectAltExt, &gnames, kSecAsn1GeneralNamesTemplate); - - return subjectAltExt; -} - -CFTypeRef kSecCSRChallengePassword = CFSTR("csrChallengePassword"); -CFTypeRef kSecSubjectAltName = CFSTR("subjectAltName"); -CFTypeRef kSecCertificateKeyUsage = CFSTR("keyUsage"); -CFTypeRef kSecCSRBasicContraintsPathLen = CFSTR("basicConstraints"); -CFTypeRef kSecCertificateExtensions = CFSTR("certificateExtensions"); - -static const uint8_t pkcs9ExtensionsRequested[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 14 }; -static const uint8_t pkcs9ChallengePassword[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 7 }; - -static const uint8_t encoded_asn1_true = 0xFF; -static const SecAsn1Item asn1_true = - { sizeof(encoded_asn1_true), (uint8_t*)&encoded_asn1_true }; - -static inline uint32_t highest_bit(uint32_t n) -{ - return ((n) >> 16 ? ((n)>>=16, 16) : 0) + \ - ((n) >> 8 ? ((n)>>=8, 8) : 0) + \ - ((n) >> 4 ? ((n)>>=4, 4) : 0) + \ - ((n) >> 2 ? ((n)>>=2, 2) : 0) + \ - ((n) >> 1 ? ((n)>>=1, 1) : 0) + \ - (n); -} - -struct add_custom_extension_args { - PLArenaPool *poolp; - NSS_CertExtension *csr_extension; - uint32_t num_extensions; - uint32_t max_extensions; -}; - -static void add_custom_extension(const void *key, const void *value, void *context) -{ - struct add_custom_extension_args *args = (struct add_custom_extension_args *)context; - size_t der_data_len; - - require(args->num_extensions < args->max_extensions, out); - - uint8_t * der_data = oid_der_data(args->poolp, key, &der_data_len); - SecAsn1Item encoded_value = {}; - - if (CFGetTypeID(value) == CFStringGetTypeID()) { - CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), kCFStringEncodingUTF8); - char *buffer = (char *)PORT_ArenaZNewArray(args->poolp, uint8_t, buffer_size); - if (!CFStringGetCString(value, buffer, buffer_size, kCFStringEncodingUTF8)) - goto out; - - SecAsn1Item buffer_item = { strlen(buffer), (uint8_t*)buffer }; - SEC_ASN1EncodeItem(args->poolp, &encoded_value, &buffer_item, kSecAsn1UTF8StringTemplate); - } else if (CFGetTypeID(value) == CFDataGetTypeID()) { - SecAsn1Item data_item = { CFDataGetLength(value), (uint8_t*)CFDataGetBytePtr(value) }; - SEC_ASN1EncodeItem(args->poolp, &encoded_value, &data_item, kSecAsn1OctetStringTemplate); - } else - goto out; - - - if (der_data && encoded_value.Length) { - args->csr_extension[args->num_extensions].value = encoded_value; - args->csr_extension[args->num_extensions].extnId.Length = der_data_len; - args->csr_extension[args->num_extensions].extnId.Data = der_data; - args->num_extensions++; - } -out: - return; -} - -static -NSS_CertExtension ** -extensions_from_parameters(PRArenaPool *poolp, CFDictionaryRef parameters) -{ - uint32_t num_extensions = 0, max_extensions = 10; - NSS_CertExtension **csr_extensions = PORT_ArenaZNewArray(poolp, NSS_CertExtension *, max_extensions + 1); /* NULL terminated array */ - NSS_CertExtension *csr_extension = PORT_ArenaZNewArray(poolp, NSS_CertExtension, max_extensions); - - CFNumberRef basic_contraints_num = CFDictionaryGetValue(parameters, kSecCSRBasicContraintsPathLen); - if (basic_contraints_num) { - NSS_BasicConstraints basic_contraints = { asn1_true, {} }; - uint8_t path_len; - - int basic_contraints_path_len = 0; - require(CFNumberGetValue(basic_contraints_num, kCFNumberIntType, &basic_contraints_path_len), out); - if (basic_contraints_path_len >= 0 && basic_contraints_path_len < 256) { - path_len = (uint8_t)basic_contraints_path_len; - basic_contraints.pathLenConstraint.Length = sizeof(path_len); - basic_contraints.pathLenConstraint.Data = &path_len; - } - - csr_extension[num_extensions].extnId.Data = oidBasicConstraints.data; - csr_extension[num_extensions].extnId.Length = oidBasicConstraints.length; - csr_extension[num_extensions].critical = asn1_true; - - SEC_ASN1EncodeItem(poolp, &csr_extension[num_extensions].value, &basic_contraints, - kSecAsn1BasicConstraintsTemplate); - require(num_extensions++ < max_extensions, out); - } - - CFDictionaryRef subject_alternate_names = CFDictionaryGetValue(parameters, kSecSubjectAltName); - if (subject_alternate_names) { - require(CFGetTypeID(subject_alternate_names) == CFDictionaryGetTypeID(), out); - csr_extension[num_extensions].value = make_subjectAltName_extension(poolp, subject_alternate_names); - /* set up subjectAltName cert request value */ - csr_extension[num_extensions].extnId.Length = oidSubjectAltName.length; - csr_extension[num_extensions].extnId.Data = oidSubjectAltName.data; - require(num_extensions++ < max_extensions, out); - } - - CFNumberRef key_usage_requested = CFDictionaryGetValue(parameters, kSecCertificateKeyUsage); - SecAsn1Item key_usage_asn1_value = { 0 }; - if (key_usage_requested) { - int key_usage_value; - require(CFNumberGetValue(key_usage_requested, kCFNumberIntType, &key_usage_value), out); - if (key_usage_value > 0) { - uint32_t key_usage_value_be = 0, key_usage_mask = 1<<31; - uint32_t key_usage_value_max_bitlen = 9, key_usage_value_bitlen = 0; - while(key_usage_value_max_bitlen) { - if (key_usage_value & 1) { - key_usage_value_be |= key_usage_mask; - key_usage_value_bitlen = 10 - key_usage_value_max_bitlen; - } - key_usage_value >>= 1; - key_usage_value_max_bitlen--; - key_usage_mask >>= 1; - } - - SecAsn1Item key_usage_input = { key_usage_value_bitlen, - ((uint8_t*)&key_usage_value_be) + 3 - (key_usage_value_bitlen >> 3) }; - SEC_ASN1EncodeItem(poolp, &key_usage_asn1_value, &key_usage_input, kSecAsn1BitStringTemplate); - - csr_extension[num_extensions].extnId.Data = oidKeyUsage.data; - csr_extension[num_extensions].extnId.Length = oidKeyUsage.length; - csr_extension[num_extensions].critical = asn1_true; - csr_extension[num_extensions].value = key_usage_asn1_value; - require(num_extensions++ < max_extensions, out); - } - } - - CFDictionaryRef custom_extension_requested = CFDictionaryGetValue(parameters, kSecCertificateExtensions); - if (custom_extension_requested) { - require(CFGetTypeID(custom_extension_requested) == CFDictionaryGetTypeID(), out); - struct add_custom_extension_args args = { - poolp, - csr_extension, - num_extensions, - max_extensions - }; - CFDictionaryApplyFunction(custom_extension_requested, add_custom_extension, &args); - num_extensions = args.num_extensions; - } - - /* extensions requested (subjectAltName, keyUsage) sequence of extension sequences */ - uint32_t ix = 0; - for (ix = 0; ix < num_extensions; ix++) - csr_extensions[ix] = csr_extension[ix].extnId.Length ? &csr_extension[ix] : NULL; - -out: - return csr_extensions; -} - - - -static -NSS_Attribute **nss_attributes_from_parameters_dict(PRArenaPool *poolp, CFDictionaryRef parameters) -{ - /* A challenge-password attribute must have a single attribute value. - - ChallengePassword attribute values generated in accordance with this - version of this document SHOULD use the PrintableString encoding - whenever possible. If internationalization issues make this - impossible, the UTF8String alternative SHOULD be used. PKCS #9- - attribute processing systems MUST be able to recognize and process - all string types in DirectoryString values. - - Upperbound of 255 defined for all PKCS#9 attributes. - - pkcs-9 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840) - rsadsi(113549) pkcs(1) 9} - pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7} - - */ - if (!parameters) - return NULL; - uint32_t num_attrs = 0; - - CFStringRef challenge = CFDictionaryGetValue(parameters, kSecCSRChallengePassword); - NSS_Attribute challenge_password_attr = {}; - if (challenge) { - CFIndex buffer_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(challenge), - kCFStringEncodingUTF8); /* we only allow UTF8 or ASCII */ - char *buffer = (char *)PORT_ArenaZNewArray(poolp, uint8_t, buffer_size); - bool utf8 = false; - if (!CFStringGetCString(challenge, buffer, buffer_size, kCFStringEncodingASCII)) { - if (!CFStringGetCString(challenge, buffer, buffer_size, kCFStringEncodingUTF8)) - return NULL; - utf8 = true; - } else - if (!printable_string(challenge)) - utf8 = true; - - SecAsn1Item *challenge_password_value = PORT_ArenaZNewArray(poolp, SecAsn1Item, 1); - SecAsn1Item challenge_password_raw = { strlen(buffer), (uint8_t*)buffer }; - SEC_ASN1EncodeItem(poolp, challenge_password_value, &challenge_password_raw, - utf8 ? kSecAsn1UTF8StringTemplate : kSecAsn1PrintableStringTemplate); - SecAsn1Item **challenge_password_values = PORT_ArenaZNewArray(poolp, SecAsn1Item *, 2); - challenge_password_values[0] = challenge_password_value; - challenge_password_attr.attrType.Length = sizeof(pkcs9ChallengePassword); - challenge_password_attr.attrType.Data = (uint8_t*)&pkcs9ChallengePassword; - challenge_password_attr.attrValue = challenge_password_values; - num_attrs++; - } - - NSS_CertExtension **extensions = extensions_from_parameters(poolp, parameters); - NSS_Attribute extensions_requested_attr = {}; - if (extensions) { - SecAsn1Item *extensions_requested_value = PORT_ArenaZNewArray(poolp, SecAsn1Item, 1); - SEC_ASN1EncodeItem(poolp, extensions_requested_value, &extensions, kSecAsn1SequenceOfCertExtensionTemplate); - SecAsn1Item **extensions_requested_values = PORT_ArenaZNewArray(poolp, SecAsn1Item *, 2); - extensions_requested_values[0] = extensions_requested_value; - extensions_requested_values[1] = NULL; - extensions_requested_attr.attrType.Length = sizeof(pkcs9ExtensionsRequested); - extensions_requested_attr.attrType.Data = (uint8_t*)pkcs9ExtensionsRequested; - extensions_requested_attr.attrValue = extensions_requested_values; - num_attrs++; - } - - NSS_Attribute **attributes_ptr = PORT_ArenaZNewArray(poolp, NSS_Attribute *, num_attrs + 1); - NSS_Attribute *attributes = PORT_ArenaZNewArray(poolp, NSS_Attribute, num_attrs); - if (challenge_password_attr.attrType.Length) { - --num_attrs; - attributes[num_attrs] = challenge_password_attr; - attributes_ptr[num_attrs] = &attributes[num_attrs]; - } - if (extensions_requested_attr.attrType.Length) { - --num_attrs; - attributes[num_attrs] = extensions_requested_attr; - attributes_ptr[num_attrs] = &attributes[num_attrs]; - } - return attributes_ptr; -#if 0 -out: - return NULL; -#endif -} - -static const uint8_t encoded_null[2] = { SEC_ASN1_NULL, 0 }; -static const SecAsn1Item asn1_null = { sizeof(encoded_null), (uint8_t*)encoded_null }; - -CFDataRef SecGenerateCertificateRequestWithParameters(SecRDN *subject, - CFDictionaryRef parameters, SecKeyRef publicKey, SecKeyRef privateKey) -{ - CFDataRef csr = NULL; - PRArenaPool *poolp = PORT_NewArena(1024); - CFDictionaryRef pubkey_attrs = NULL; - - if (!poolp) - return NULL; - - NSSCertRequest certReq; - memset(&certReq, 0, sizeof(certReq)); - - /* version */ - unsigned char version = 0; - certReq.reqInfo.version.Length = sizeof(version); - certReq.reqInfo.version.Data = &version; - - /* subject */ - unsigned atv_num = 0, num = 0; - SecRDN *one_rdn; - SecATV *one_atv; - for (one_rdn = subject; *one_rdn; one_rdn++) { - for (one_atv = *one_rdn; one_atv->oid; one_atv++) - atv_num++; - atv_num++; /* one more */ - num++; - } - NSS_ATV atvs[atv_num]; - NSS_ATV *atvps[atv_num]; - NSS_RDN rdns[num]; - NSS_RDN *rdnps[num+1]; - atv_num = 0; - unsigned rdn_num = 0; - for (one_rdn = subject; *one_rdn; one_rdn++) { - rdns[rdn_num].atvs = &atvps[atv_num]; - rdnps[rdn_num] = &rdns[rdn_num]; - rdn_num++; - for (one_atv = *one_rdn; one_atv->oid; one_atv++) { - if (!make_nss_atv(poolp, one_atv->oid, one_atv->value, - one_atv->type, &atvs[atv_num])) - return NULL; - atvps[atv_num] = &atvs[atv_num]; - atv_num++; - } - atvps[atv_num++] = NULL; - } - rdnps[rdn_num] = NULL; - certReq.reqInfo.subject.rdns = rdnps; - - /* public key info */ - certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Length = oidRsa.length; - certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Data = oidRsa.data; - certReq.reqInfo.subjectPublicKeyInfo.algorithm.parameters = asn1_null; - - pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey); - CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData); - uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)]; - size_t signature_length = sizeof(signature); - - certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey); - certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey); - - certReq.reqInfo.attributes = nss_attributes_from_parameters_dict(poolp, parameters); - SecCmsArraySortByDER((void **)certReq.reqInfo.attributes, kSecAsn1AttributeTemplate, NULL); - - /* encode request info by itself to calculate signature */ - SecAsn1Item reqinfo = {}; - SEC_ASN1EncodeItem(poolp, &reqinfo, &certReq.reqInfo, kSecAsn1CertRequestInfoTemplate); - - /* calculate signature */ - uint8_t reqinfo_hash[CC_SHA1_DIGEST_LENGTH]; - CCDigest(kCCDigestSHA1, reqinfo.Data, (CC_LONG)reqinfo.Length, reqinfo_hash); - require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1, - reqinfo_hash, sizeof(reqinfo_hash), signature, &signature_length), out); - - /* signature and info */ - certReq.signatureAlgorithm.algorithm.Length = oidSha1Rsa.length; - certReq.signatureAlgorithm.algorithm.Data = oidSha1Rsa.data; - certReq.signatureAlgorithm.parameters = asn1_null; - certReq.signature.Data = signature; - certReq.signature.Length = signature_length * 8; - - /* encode csr */ - SecAsn1Item cert_request = {}; - require_quiet(SEC_ASN1EncodeItem(poolp, &cert_request, &certReq, - kSecAsn1CertRequestTemplate), out); - csr = CFDataCreate(kCFAllocatorDefault, cert_request.Data, cert_request.Length); - -out: - if (poolp) - PORT_FreeArena(poolp, PR_TRUE); - CFReleaseSafe(pubkey_attrs); - return csr; -} - -CFDataRef SecGenerateCertificateRequest(CFArrayRef subject, - CFDictionaryRef parameters, SecKeyRef publicKey, SecKeyRef privateKey) -{ - CFDataRef csr = NULL; - PRArenaPool *poolp = PORT_NewArena(1024); - CFDictionaryRef pubkey_attrs = NULL; - - if (!poolp) - return NULL; - - NSSCertRequest certReq; - memset(&certReq, 0, sizeof(certReq)); - - /* version */ - unsigned char version = 0; - certReq.reqInfo.version.Length = sizeof(version); - certReq.reqInfo.version.Data = &version; - - /* subject */ - certReq.reqInfo.subject.rdns = make_subject(poolp, (CFArrayRef)subject); - - /* public key info */ - certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Length = oidRsa.length; - certReq.reqInfo.subjectPublicKeyInfo.algorithm.algorithm.Data = oidRsa.data; - certReq.reqInfo.subjectPublicKeyInfo.algorithm.parameters = asn1_null; - - pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey); - CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData); - uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)]; - size_t signature_length = sizeof(signature); - - certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey); - certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey); - - certReq.reqInfo.attributes = nss_attributes_from_parameters_dict(poolp, parameters); - SecCmsArraySortByDER((void **)certReq.reqInfo.attributes, kSecAsn1AttributeTemplate, NULL); - - /* encode request info by itself to calculate signature */ - SecAsn1Item reqinfo = {}; - SEC_ASN1EncodeItem(poolp, &reqinfo, &certReq.reqInfo, kSecAsn1CertRequestInfoTemplate); - - /* calculate signature */ - uint8_t reqinfo_hash[CC_SHA1_DIGEST_LENGTH]; - CCDigest(kCCDigestSHA1, reqinfo.Data, reqinfo.Length, reqinfo_hash); - require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1, - reqinfo_hash, sizeof(reqinfo_hash), signature, &signature_length), out); - - /* signature and info */ - certReq.signatureAlgorithm.algorithm.Length = oidSha1Rsa.length; - certReq.signatureAlgorithm.algorithm.Data = oidSha1Rsa.data; - certReq.signatureAlgorithm.parameters = asn1_null; - certReq.signature.Data = signature; - certReq.signature.Length = signature_length * 8; - - /* encode csr */ - SecAsn1Item cert_request = {}; - require_quiet(SEC_ASN1EncodeItem(poolp, &cert_request, &certReq, - kSecAsn1CertRequestTemplate), out); - csr = CFDataCreate(kCFAllocatorDefault, cert_request.Data, cert_request.Length); - -out: - if (poolp) - PORT_FreeArena(poolp, PR_TRUE); - CFReleaseSafe(pubkey_attrs); - return csr; -} - -bool SecVerifyCertificateRequest(CFDataRef csr, SecKeyRef *publicKey, - CFStringRef *challenge, CFDataRef *subject, CFDataRef *extensions) -{ - PRArenaPool *poolp = PORT_NewArena(1024); - SecKeyRef candidatePublicKey = NULL; - bool valid = false; - NSSCertRequest certReq; - memset(&certReq, 0, sizeof(certReq)); - SecAsn1Item csr_item = { CFDataGetLength(csr), (uint8_t*)CFDataGetBytePtr(csr) }; - require_noerr_quiet(SEC_ASN1DecodeItem(poolp, &certReq, kSecAsn1CertRequestTemplate, - &csr_item), out); - - /* signature and info */ - require(certReq.signatureAlgorithm.algorithm.Length == oidSha1Rsa.length, out); - require_noerr(memcmp(oidSha1Rsa.data, certReq.signatureAlgorithm.algorithm.Data, - oidSha1Rsa.length), out); - require(certReq.signatureAlgorithm.parameters.Length == asn1_null.Length, out); - require_noerr(memcmp(asn1_null.Data, certReq.signatureAlgorithm.parameters.Data, - asn1_null.Length), out); - - /* encode request info by itself to calculate signature */ - SecAsn1Item reqinfo = {}; - SEC_ASN1EncodeItem(poolp, &reqinfo, &certReq.reqInfo, kSecAsn1CertRequestInfoTemplate); - - /* calculate signature */ - uint8_t reqinfo_hash[CC_SHA1_DIGEST_LENGTH]; - require(reqinfo.Length<=UINT32_MAX, out); - CCDigest(kCCDigestSHA1, reqinfo.Data, (CC_LONG)reqinfo.Length, reqinfo_hash); - - /* @@@ check for version 0 */ - - require(candidatePublicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault, - certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Data, - certReq.reqInfo.subjectPublicKeyInfo.subjectPublicKey.Length / 8, - kSecKeyEncodingPkcs1), out); - - require_noerr_quiet(SecKeyRawVerify(candidatePublicKey, kSecPaddingPKCS1SHA1, - reqinfo_hash, sizeof(reqinfo_hash), - certReq.signature.Data, certReq.signature.Length / 8), out); - - SecAsn1Item subject_item = { 0 }, extensions_item = { 0 }, challenge_item = { 0 }; - require_quiet(SEC_ASN1EncodeItem(poolp, &subject_item, - &certReq.reqInfo.subject, kSecAsn1NameTemplate), out); - - if (*certReq.reqInfo.attributes) { - uint32_t ix; - for (ix = 0; certReq.reqInfo.attributes[ix]; ix++) { - NSS_Attribute *attr = certReq.reqInfo.attributes[ix]; - if ( (sizeof(pkcs9ChallengePassword) == attr->attrType.Length) && - !memcmp(pkcs9ChallengePassword, attr->attrType.Data, sizeof(pkcs9ChallengePassword))) - challenge_item = *attr->attrValue[0]; - else if ( (sizeof(pkcs9ExtensionsRequested) == attr->attrType.Length) && - !memcmp(pkcs9ExtensionsRequested, attr->attrType.Data, sizeof(pkcs9ExtensionsRequested))) - extensions_item = *attr->attrValue[0]; - } - } - - if (subject && subject_item.Length) - *subject = CFDataCreate(kCFAllocatorDefault, subject_item.Data, subject_item.Length); - if (extensions && extensions_item.Length) - *extensions = CFDataCreate(kCFAllocatorDefault, extensions_item.Data, extensions_item.Length); - if (challenge && challenge_item.Length) { - SecAsn1Item string = { 0 }; - SECStatus rv = SEC_ASN1DecodeItem(poolp, &string, kSecAsn1UTF8StringTemplate, &challenge_item); - if (rv) - rv = SEC_ASN1DecodeItem(poolp, &string, kSecAsn1PrintableStringTemplate, &challenge_item); - if (!rv) - *challenge = CFStringCreateWithBytes(kCFAllocatorDefault, string.Data, string.Length, kCFStringEncodingUTF8, false); - else - *challenge = NULL; - } - if (publicKey) { - *publicKey = candidatePublicKey; - candidatePublicKey = NULL; - } - valid = true; -out: - CFReleaseSafe(candidatePublicKey); - if (poolp) - PORT_FreeArena(poolp, PR_TRUE); - return valid; -} - -#define HIDIGIT(v) (((v) / 10) + '0') -#define LODIGIT(v) (((v) % 10) + '0') - -static OSStatus -DER_CFDateToUTCTime(PRArenaPool *poolp, CFAbsoluteTime date, SecAsn1Item * utcTime) -{ - CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(date, NULL /* GMT */); - unsigned char *d; - SInt8 second; - - utcTime->Length = 13; - utcTime->Data = d = PORT_ArenaAlloc(poolp, 13); - if (!utcTime->Data) - return SECFailure; - - /* UTC time does not handle the years before 1950 */ - if (gdate.year < 1950) - return SECFailure; - - /* remove the century since it's added to the year by the - CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */ - gdate.year %= 100; - second = gdate.second; - - d[0] = HIDIGIT(gdate.year); - d[1] = LODIGIT(gdate.year); - d[2] = HIDIGIT(gdate.month); - d[3] = LODIGIT(gdate.month); - d[4] = HIDIGIT(gdate.day); - d[5] = LODIGIT(gdate.day); - d[6] = HIDIGIT(gdate.hour); - d[7] = LODIGIT(gdate.hour); - d[8] = HIDIGIT(gdate.minute); - d[9] = LODIGIT(gdate.minute); - d[10] = HIDIGIT(second); - d[11] = LODIGIT(second); - d[12] = 'Z'; - return SECSuccess; -} - -SecCertificateRef -SecGenerateSelfSignedCertificate(CFArrayRef subject, CFDictionaryRef parameters, - SecKeyRef publicKey, SecKeyRef privateKey) -{ - SecCertificateRef cert = NULL; - PRArenaPool *poolp = PORT_NewArena(1024); - CFDictionaryRef pubkey_attrs = NULL; - if (!poolp) - return NULL; - - NSS_Certificate cert_tmpl; - memset(&cert_tmpl, 0, sizeof(cert_tmpl)); - - /* version */ - unsigned char version = 2; - cert_tmpl.tbs.version.Length = sizeof(version); - cert_tmpl.tbs.version.Data = &version; - - /* serialno */ - unsigned char serialNumber = 1; - cert_tmpl.tbs.serialNumber.Length = sizeof(serialNumber); - cert_tmpl.tbs.serialNumber.Data = &serialNumber; - - /* subject/issuer */ - cert_tmpl.tbs.issuer.rdns = make_subject(poolp, (CFArrayRef)subject); - cert_tmpl.tbs.subject.rdns = cert_tmpl.tbs.issuer.rdns; - - DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent(), &cert_tmpl.tbs.validity.notBefore.item); - cert_tmpl.tbs.validity.notBefore.tag = SEC_ASN1_UTC_TIME; - DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl.tbs.validity.notAfter.item); - cert_tmpl.tbs.validity.notAfter.tag = SEC_ASN1_UTC_TIME; - - /* extensions */ - cert_tmpl.tbs.extensions = extensions_from_parameters(poolp, parameters); - - /* @@@ we only handle rsa keys */ - pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey); - CFTypeRef key_type = CFDictionaryGetValue(pubkey_attrs, kSecAttrKeyType); - if (key_type && CFEqual(key_type, kSecAttrKeyTypeRSA)) { - /* public key data and algorithm */ - cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.algorithm = CSSMOID_RSA; - cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.parameters = asn1_null; - - CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData); - cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey); - cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey); - - /* signature algorithm */ - cert_tmpl.tbs.signature.algorithm = CSSMOID_SHA1WithRSA; - cert_tmpl.tbs.signature.parameters = asn1_null; - cert_tmpl.signatureAlgorithm.algorithm = CSSMOID_SHA1WithRSA; - cert_tmpl.signatureAlgorithm.parameters = asn1_null; - - /* encode request info by itself to calculate signature */ - SecAsn1Item tbscert = {}; - SEC_ASN1EncodeItem(poolp, &tbscert, &cert_tmpl.tbs, kSecAsn1TBSCertificateTemplate); - - /* calculate signature */ - uint8_t tbscert_hash[CC_SHA1_DIGEST_LENGTH]; - CCDigest(kCCDigestSHA1, tbscert.Data, tbscert.Length, tbscert_hash); - uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)]; - size_t signature_length = sizeof(signature); - require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1, - tbscert_hash, sizeof(tbscert_hash), signature, &signature_length), out); - - /* signature */ - cert_tmpl.signature.Data = signature; - cert_tmpl.signature.Length = signature_length * 8; - - /* encode cert */ - SecAsn1Item signed_cert = {}; - require_quiet(SEC_ASN1EncodeItem(poolp, &signed_cert, &cert_tmpl, - kSecAsn1SignedCertTemplate), out); - cert = SecCertificateCreateWithBytes(kCFAllocatorDefault, - signed_cert.Data, signed_cert.Length); - } -out: - if (poolp) - PORT_FreeArena(poolp, PR_TRUE); - CFReleaseSafe(pubkey_attrs); - return cert; -} - - -SecCertificateRef -SecIdentitySignCertificate(SecIdentityRef issuer, CFDataRef serialno, - SecKeyRef publicKey, CFTypeRef subject, CFTypeRef extensions) -{ - SecCertificateRef cert = NULL; - SecKeyRef privateKey = NULL; - - PRArenaPool *poolp = PORT_NewArena(1024); - CFDictionaryRef pubkey_attrs = NULL; - if (!poolp) - return NULL; - - NSS_Certificate cert_tmpl; - memset(&cert_tmpl, 0, sizeof(cert_tmpl)); - - /* version */ - unsigned char version = 2; - cert_tmpl.tbs.version.Length = sizeof(version); - cert_tmpl.tbs.version.Data = &version; - - /* serialno */ - cert_tmpl.tbs.serialNumber.Length = CFDataGetLength(serialno); - cert_tmpl.tbs.serialNumber.Data = (uint8_t*)CFDataGetBytePtr(serialno); - - /* subject/issuer */ - if (CFArrayGetTypeID() == CFGetTypeID(subject)) - cert_tmpl.tbs.subject.rdns = make_subject(poolp, (CFArrayRef)subject); - else if (CFDataGetTypeID() == CFGetTypeID(subject)) { - SecAsn1Item subject_item = { CFDataGetLength(subject), (uint8_t*)CFDataGetBytePtr(subject) }; - require_noerr_quiet(SEC_ASN1DecodeItem(poolp, &cert_tmpl.tbs.subject.rdns, kSecAsn1NameTemplate, &subject_item), out); - } else - goto out; - - SecCertificateRef issuer_cert = NULL; - require_noerr(SecIdentityCopyCertificate(issuer, &issuer_cert), out); - CFDataRef issuer_name = SecCertificateCopySubjectSequence(issuer_cert); - SecAsn1Item issuer_item = { CFDataGetLength(issuer_name), (uint8_t*)CFDataGetBytePtr(issuer_name) }; - require_noerr_action_quiet(SEC_ASN1DecodeItem(poolp, &cert_tmpl.tbs.issuer.rdns, - kSecAsn1NameTemplate, &issuer_item), out, CFReleaseNull(issuer_name)); - CFReleaseNull(issuer_name); - - DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent(), &cert_tmpl.tbs.validity.notBefore.item); - cert_tmpl.tbs.validity.notBefore.tag = SEC_ASN1_UTC_TIME; - DER_CFDateToUTCTime(poolp, CFAbsoluteTimeGetCurrent() + 3600*24*365, &cert_tmpl.tbs.validity.notAfter.item); - cert_tmpl.tbs.validity.notAfter.tag = SEC_ASN1_UTC_TIME; - - /* extensions */ - if (extensions) { - if (CFDataGetTypeID() == CFGetTypeID(extensions)) { - SecAsn1Item requested_extensions = { CFDataGetLength(extensions), (uint8_t*)CFDataGetBytePtr(extensions) }; - //NSS_CertExtension **requested_extensions_decoded; - require_noerr_quiet(SEC_ASN1DecodeItem(poolp, &cert_tmpl.tbs.extensions, - kSecAsn1SequenceOfCertExtensionTemplate, &requested_extensions), out); - } else if (CFDictionaryGetTypeID() == CFGetTypeID(extensions)) { - cert_tmpl.tbs.extensions = extensions_from_parameters(poolp, extensions); - } - } - - /* @@@ we only handle rsa keys */ - pubkey_attrs = SecKeyCopyAttributeDictionary(publicKey); - CFTypeRef key_type = CFDictionaryGetValue(pubkey_attrs, kSecAttrKeyType); - if (key_type && CFEqual(key_type, kSecAttrKeyTypeRSA)) { - /* public key data and algorithm */ - cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.algorithm = CSSMOID_RSA; - cert_tmpl.tbs.subjectPublicKeyInfo.algorithm.parameters = asn1_null; - - CFDataRef pkcs1_pubkey = (CFDataRef)CFDictionaryGetValue(pubkey_attrs, kSecValueData); - cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Length = 8 * CFDataGetLength(pkcs1_pubkey); - cert_tmpl.tbs.subjectPublicKeyInfo.subjectPublicKey.Data = (uint8_t*)CFDataGetBytePtr(pkcs1_pubkey); - - /* signature algorithm */ - cert_tmpl.tbs.signature.algorithm = CSSMOID_SHA1WithRSA; - cert_tmpl.tbs.signature.parameters = asn1_null; - cert_tmpl.signatureAlgorithm.algorithm = CSSMOID_SHA1WithRSA; - cert_tmpl.signatureAlgorithm.parameters = asn1_null; - - /* encode request info by itself to calculate signature */ - SecAsn1Item tbscert = {}; - SEC_ASN1EncodeItem(poolp, &tbscert, &cert_tmpl.tbs, kSecAsn1TBSCertificateTemplate); - - /* calculate signature */ - uint8_t tbscert_hash[CC_SHA1_DIGEST_LENGTH]; - CCDigest(kCCDigestSHA1, tbscert.Data, tbscert.Length, tbscert_hash); - uint8_t signature[8 * CFDataGetLength(pkcs1_pubkey)]; - size_t signature_length = sizeof(signature); - - require_noerr_quiet(SecIdentityCopyPrivateKey(issuer, &privateKey), out); - require_noerr_quiet(SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1, - tbscert_hash, sizeof(tbscert_hash), signature, &signature_length), out); - - /* signature */ - cert_tmpl.signature.Data = signature; - cert_tmpl.signature.Length = signature_length * 8; - - /* encode cert */ - SecAsn1Item signed_cert = {}; - require_quiet(SEC_ASN1EncodeItem(poolp, &signed_cert, &cert_tmpl, - kSecAsn1SignedCertTemplate), out); - cert = SecCertificateCreateWithBytes(kCFAllocatorDefault, - signed_cert.Data, signed_cert.Length); - } -out: - CFReleaseSafe(privateKey); - if (poolp) - PORT_FreeArena(poolp, PR_TRUE); - CFReleaseSafe(pubkey_attrs); - return cert; -} - -CFDataRef -SecGenerateCertificateRequestSubject(SecCertificateRef ca_certificate, CFArrayRef subject) -{ - CFMutableDataRef sequence = NULL; - PRArenaPool *poolp = PORT_NewArena(1024); - if (!poolp) - return NULL; - - /* - Going agains the spec here: - - 3.2.3. GetCertInitial - - The messageData for this type consists of a DER-encoded - IssuerAndSubject (Section 3.2.3.1). The issuer is set to the - issuerName from the certification authority from which we are issued - certificates. The Subject is set to the SubjectName we used when - requesting the certificate. - - That clearly says use the issuer of the cert issuing certificate. Since - it is combined with the subject of the to-be-issued certificate, that - seems a mistake. If we take the subject of the issuer and the subject - of the certificate we're interested in, we get the issuer and subject - the certificate to be returned will have. - - */ - CFDataRef issuer_sequence = SecCertificateCopySubjectSequence(ca_certificate); - SecAsn1Item subject_item = { 0 }; - SecAsn1Item issuer_item = { CFDataGetLength(issuer_sequence), (uint8_t*)CFDataGetBytePtr(issuer_sequence) }; - NSS_Name nss_subject = { make_subject(poolp, subject) }; - require_quiet(SEC_ASN1EncodeItem(poolp, &subject_item, &nss_subject, kSecAsn1NameTemplate), out); - - DERSize sequence_length = DERLengthOfLength(subject_item.Length + issuer_item.Length); - DERSize seq_len_length = subject_item.Length + issuer_item.Length + 1 /* SEQUENCE */ + - sequence_length; - sequence = CFDataCreateMutable(kCFAllocatorDefault, 0); - CFDataSetLength(sequence, seq_len_length); - uint8_t *sequence_ptr = CFDataGetMutableBytePtr(sequence); - *sequence_ptr++ = 0x30; //ASN1_CONSTR_SEQUENCE; - require_noerr_quiet(DEREncodeLength(subject_item.Length + issuer_item.Length, sequence_ptr, &sequence_length), out); - sequence_ptr += sequence_length; - memcpy(sequence_ptr, issuer_item.Data, issuer_item.Length); - memcpy(sequence_ptr + issuer_item.Length, subject_item.Data, subject_item.Length); - -out: - CFReleaseSafe(issuer_sequence); - if (poolp) - PORT_FreeArena(poolp, PR_TRUE); - return sequence; -}