+++ /dev/null
-/*
- * 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 <libDER/oids.h>
-#include <libDER/DER_Encode.h>
-
-#include <security_asn1/SecAsn1Types.h>
-#include <security_asn1/csrTemplates.h>
-#include <security_asn1/certExtensionTemplates.h>
-#include <security_asn1/secasn1.h>
-#include <security_asn1/SecAsn1Types.h>
-#include <security_asn1/oidsalg.h>
-#include <security_asn1/nameTemplates.h>
-
-#include <TargetConditionals.h>
-#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 <security_smime/cmspriv.h>
-#endif
-
-#include <Security/SecInternal.h>
-#include <Security/SecCertificatePriv.h>
-#include <Security/SecIdentity.h>
-#include <Security/SecCertificateInternal.h>
-#include <Security/SecItem.h>
-#include <Security/SecKey.h>
-#include <Security/SecRSAKey.h>
-#include <Security/SecKeyPriv.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonDigestSPI.h>
-#include <CoreFoundation/CFNumber.h>
-#include <CoreFoundation/CFString.h>
-
-#include <AssertMacros.h>
-
-#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;
-}