/** * @file pkcs10.c * @brief Functions to build PKCS#10 requests * * Contains functions to build DER encoded pkcs#10 certificate requests */ /* Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include #include #include #include #include #include "../pluto/constants.h" #include "../pluto/defs.h" #include "../pluto/asn1.h" #include "../pluto/pkcs1.h" #include "../pluto/log.h" #include "../pluto/x509.h" #include "pkcs10.h" /* some pre-coded OIDs */ static u_char ASN1_challengePassword_oid_str[] = { 0x06,0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x07 }; static const chunk_t ASN1_challengePassword_oid = strchunk(ASN1_challengePassword_oid_str); static u_char ASN1_extensionRequest_oid_str[] = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x0E }; static const chunk_t ASN1_extensionRequest_oid = strchunk(ASN1_extensionRequest_oid_str); /** * @brief Adds a subjectAltName in DER-coded form to a linked list * * @param[in,out] subjectAltNames head of the linked list of subjectAltNames * @param[in] kind type of the subjectAltName (which is a generalName) * @param[in] value value of the subjectAltName as an ASCII string */ void pkcs10_add_subjectAltName(generalName_t **subjectAltNames, generalNames_t kind , char *value) { generalName_t *gn; asn1_t asn1_type = ASN1_EOC; chunk_t name = { value, strlen(value) }; switch (kind) { case GN_RFC822_NAME: asn1_type = ASN1_CONTEXT_S_1; break; case GN_DNS_NAME: asn1_type = ASN1_CONTEXT_S_2; break; case GN_IP_ADDRESS: { struct in_addr addr; /* convert an ASCII dotted IPv4 address (e.g. 123.456.78.90) * to a byte representation in network order */ if (!inet_aton(value, &addr)) { fprintf(stderr, "error in IPv4 subjectAltName\n"); return; } asn1_type = ASN1_CONTEXT_S_7; name.ptr = (u_char *) &addr.s_addr; name.len = sizeof(addr.s_addr); break; } default: break; } gn = alloc_thing(generalName_t, "subjectAltName"); gn->kind = kind; gn->name = asn1_simple_object(asn1_type, name); gn->next = *subjectAltNames; *subjectAltNames = gn; } /** * @brief Builds the requestInfoAttributes of the certificationRequestInfo-field * * challenge password ans subjectAltNames are only included, * when avaiable in given #pkcs10_t structure * * @param[in] pkcs10 Pointer to a #pkcs10_t structure * @return 1 if succeeded, 0 otherwise */ static chunk_t build_req_info_attributes(pkcs10_t* pkcs10) { chunk_t subjectAltNames = empty_chunk; chunk_t challengePassword = empty_chunk; if (pkcs10->subjectAltNames != NULL) { subjectAltNames = asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_extensionRequest_oid , asn1_wrap(ASN1_SET, "m" , asn1_wrap(ASN1_SEQUENCE, "m" , build_subjectAltNames(pkcs10->subjectAltNames) ) ) ); } if (pkcs10->challengePassword.len > 0) { asn1_t type = is_printablestring(pkcs10->challengePassword) ? ASN1_PRINTABLESTRING : ASN1_T61STRING; challengePassword = asn1_wrap(ASN1_SEQUENCE, "cm" , ASN1_challengePassword_oid , asn1_wrap(ASN1_SET, "m" , asn1_simple_object(type, pkcs10->challengePassword) ) ); } return asn1_wrap(ASN1_CONTEXT_C_0, "mm" , subjectAltNames , challengePassword); } /** * @brief Builds a DER-code pkcs#10 certificate request * * @param[in] pkcs10 pointer to a pkcs10_t struct * @return DER-code pkcs10 request */ static chunk_t pkcs10_build_request(pkcs10_t *pkcs10, int signature_alg) { RSA_public_key_t *rsak = (RSA_public_key_t *) pkcs10->private_key; chunk_t cert_req_info = asn1_wrap(ASN1_SEQUENCE, "ccmm" , ASN1_INTEGER_0 , pkcs10->subject , pkcs1_build_publicKeyInfo(rsak) , build_req_info_attributes(pkcs10)); chunk_t signature = pkcs1_build_signature(cert_req_info , signature_alg, pkcs10->private_key, TRUE); return asn1_wrap(ASN1_SEQUENCE, "mcm" , cert_req_info , asn1_algorithmIdentifier(signature_alg) , signature); } /** * @brief Creates a pkcs#10 certificate request object * * To create a certificate request, the RSA key and the * names to be included as subject in the certificate request * (e.g. commonName, organization) are needed. An optional challenge * password or some subjectAltNames may be included. * * @param[in] key rsakey of type #rsakey_t * @param[in] subject DER-coded subject distinguished name * @param[in] challengePassword challenge password or empty_chunk * @param[in] subjectAltNames linked list of subjectAltNames or NULL * @return pointer to a #pkcs10_t object */ pkcs10_t* pkcs10_build(RSA_private_key_t *key, chunk_t subject, chunk_t challengePassword , generalName_t *subjectAltNames, int signature_alg) { pkcs10_t *pkcs10 = alloc_thing(pkcs10_t, "pkcs10_t"); pkcs10->subject = subject; pkcs10->private_key = key; pkcs10->challengePassword = challengePassword; pkcs10->subjectAltNames = subjectAltNames; pkcs10->request = pkcs10_build_request(pkcs10, signature_alg); return pkcs10; } /** * @brief Frees the resources used by an #pkcs10_t object * * @param[in] pkcs10 #pkcs10_t to free */ void pkcs10_free(pkcs10_t *pkcs10) { if (pkcs10 != NULL) { freeanychunk(pkcs10->request); pfree(pkcs10); } }