/* * Copyright (C) 2003-2012 Free Software Foundation, Inc. * Copyright (C) 2012 Nikos Mavrogiannopoulos * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* This file contains functions to handle PKCS #10 certificate requests, see RFC 2986. */ #include #include #include #include #include #include #include #include #include "x509_int.h" #include /** * gnutls_x509_crq_init: * @crq: A pointer to the type to be initialized * * This function will initialize a PKCS#10 certificate request * structure. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_init(gnutls_x509_crq_t * crq) { int result; FAIL_IF_LIB_ERROR; *crq = gnutls_calloc(1, sizeof(gnutls_x509_crq_int)); if (!*crq) return GNUTLS_E_MEMORY_ERROR; result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-10-CertificationRequest", &((*crq)->crq)); if (result != ASN1_SUCCESS) { gnutls_assert(); gnutls_free(*crq); return _gnutls_asn2err(result); } return 0; } /** * gnutls_x509_crq_deinit: * @crq: the type to be deinitialized * * This function will deinitialize a PKCS#10 certificate request * structure. **/ void gnutls_x509_crq_deinit(gnutls_x509_crq_t crq) { if (!crq) return; if (crq->crq) asn1_delete_structure(&crq->crq); gnutls_free(crq); } #define PEM_CRQ "NEW CERTIFICATE REQUEST" #define PEM_CRQ2 "CERTIFICATE REQUEST" /** * gnutls_x509_crq_import: * @crq: The data to store the parsed certificate request. * @data: The DER or PEM encoded certificate. * @format: One of DER or PEM * * This function will convert the given DER or PEM encoded certificate * request to a #gnutls_x509_crq_t type. The output will be * stored in @crq. * * If the Certificate is PEM encoded it should have a header of "NEW * CERTIFICATE REQUEST". * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_import(gnutls_x509_crq_t crq, const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format) { int result = 0, need_free = 0; gnutls_datum_t _data; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } _data.data = data->data; _data.size = data->size; /* If the Certificate is in PEM format then decode it */ if (format == GNUTLS_X509_FMT_PEM) { /* Try the first header */ result = _gnutls_fbase64_decode(PEM_CRQ, data->data, data->size, &_data); if (result < 0) /* Go for the second header */ result = _gnutls_fbase64_decode(PEM_CRQ2, data->data, data->size, &_data); if (result < 0) { gnutls_assert(); return result; } need_free = 1; } result = _asn1_strict_der_decode(&crq->crq, _data.data, _data.size, NULL); if (result != ASN1_SUCCESS) { result = _gnutls_asn2err(result); gnutls_assert(); goto cleanup; } result = 0; cleanup: if (need_free) _gnutls_free_datum(&_data); return result; } /** * gnutls_x509_crq_get_signature_algorithm: * @crq: should contain a #gnutls_x509_cr_t type * * This function will return a value of the #gnutls_sign_algorithm_t * enumeration that is the signature algorithm that has been used to * sign this certificate request. * * Returns: a #gnutls_sign_algorithm_t value, or a negative error code on * error. * * Since: 3.4.0 **/ int gnutls_x509_crq_get_signature_algorithm(gnutls_x509_crq_t crq) { return _gnutls_x509_get_signature_algorithm(crq->crq, "signatureAlgorithm.algorithm"); } /** * gnutls_x509_crq_get_private_key_usage_period: * @crq: should contain a #gnutls_x509_crq_t type * @activation: The activation time * @expiration: The expiration time * @critical: the extension status * * This function will return the expiration and activation * times of the private key of the certificate. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE * if the extension is not present, otherwise a negative error value. **/ int gnutls_x509_crq_get_private_key_usage_period(gnutls_x509_crq_t crq, time_t * activation, time_t * expiration, unsigned int *critical) { int result, ret; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; uint8_t buf[128]; size_t buf_size = sizeof(buf); if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.16", 0, buf, &buf_size, critical); if (ret < 0) return gnutls_assert_val(ret); result = asn1_create_element (_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); ret = _gnutls_asn2err(result); goto cleanup; } result = _asn1_strict_der_decode(&c2, buf, buf_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); ret = _gnutls_asn2err(result); goto cleanup; } if (activation) *activation = _gnutls_x509_get_time(c2, "notBefore", 1); if (expiration) *expiration = _gnutls_x509_get_time(c2, "notAfter", 1); ret = 0; cleanup: asn1_delete_structure(&c2); return ret; } /** * gnutls_x509_crq_get_dn: * @crq: should contain a #gnutls_x509_crq_t type * @buf: a pointer to a structure to hold the name (may be %NULL) * @buf_size: initially holds the size of @buf * * This function will copy the name of the Certificate request subject * to the provided buffer. The name will be in the form * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC 2253. The output string * @buf will be ASCII or UTF-8 encoded, depending on the certificate * data. * * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not * long enough, and in that case the *@buf_size will be updated with * the required size. On success 0 is returned. **/ int gnutls_x509_crq_get_dn(gnutls_x509_crq_t crq, char *buf, size_t * buf_size) { if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_parse_dn(crq->crq, "certificationRequestInfo.subject.rdnSequence", buf, buf_size); } /** * gnutls_x509_crq_get_dn2: * @crq: should contain a #gnutls_x509_crq_t type * @dn: a pointer to a structure to hold the name * * This function will allocate buffer and copy the name of the Certificate * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as * described in RFC4514. The output string will be ASCII or UTF-8 * encoded, depending on the certificate data. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. and a negative error code on error. * * Since: 3.1.10 **/ int gnutls_x509_crq_get_dn2(gnutls_x509_crq_t crq, gnutls_datum_t * dn) { if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_get_dn(crq->crq, "certificationRequestInfo.subject.rdnSequence", dn); } /** * gnutls_x509_crq_get_dn_by_oid: * @crq: should contain a gnutls_x509_crq_t type * @oid: holds an Object Identifier in a null terminated string * @indx: In case multiple same OIDs exist in the RDN, this specifies * which to get. Use (0) to get the first one. * @raw_flag: If non-zero returns the raw DER data of the DN part. * @buf: a pointer to a structure to hold the name (may be %NULL) * @buf_size: initially holds the size of @buf * * This function will extract the part of the name of the Certificate * request subject, specified by the given OID. The output will be * encoded as described in RFC2253. The output string will be ASCII * or UTF-8 encoded, depending on the certificate data. * * Some helper macros with popular OIDs can be found in gnutls/x509.h * If raw flag is (0), this function will only return known OIDs as * text. Other OIDs will be DER encoded, as described in RFC2253 -- * in hex format with a '\#' prefix. You can check about known OIDs * using gnutls_x509_dn_oid_known(). * * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is * not long enough, and in that case the *@buf_size will be * updated with the required size. On success 0 is returned. **/ int gnutls_x509_crq_get_dn_by_oid(gnutls_x509_crq_t crq, const char *oid, int indx, unsigned int raw_flag, void *buf, size_t * buf_size) { gnutls_datum_t td; int ret; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = _gnutls_x509_parse_dn_oid (crq->crq, "certificationRequestInfo.subject.rdnSequence", oid, indx, raw_flag, &td); if (ret < 0) return gnutls_assert_val(ret); return _gnutls_strdatum_to_buf(&td, buf, buf_size); } /** * gnutls_x509_crq_get_dn_oid: * @crq: should contain a gnutls_x509_crq_t type * @indx: Specifies which DN OID to get. Use (0) to get the first one. * @oid: a pointer to a structure to hold the name (may be %NULL) * @sizeof_oid: initially holds the size of @oid * * This function will extract the requested OID of the name of the * certificate request subject, specified by the given index. * * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is * not long enough, and in that case the *@sizeof_oid will be * updated with the required size. On success 0 is returned. **/ int gnutls_x509_crq_get_dn_oid(gnutls_x509_crq_t crq, int indx, void *oid, size_t * sizeof_oid) { if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_get_dn_oid(crq->crq, "certificationRequestInfo.subject.rdnSequence", indx, oid, sizeof_oid); } /* Parses an Attribute list in the asn1_struct, and searches for the * given OID. The index indicates the attribute value to be returned. * * If raw==0 only printable data are returned, or * GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE. * * asn1_attr_name must be a string in the form * "certificationRequestInfo.attributes" * */ static int parse_attribute(ASN1_TYPE asn1_struct, const char *attr_name, const char *given_oid, int indx, int raw, gnutls_datum_t * out) { int k1, result; char tmpbuffer1[ASN1_MAX_NAME_SIZE]; char tmpbuffer3[ASN1_MAX_NAME_SIZE]; char value[200]; gnutls_datum_t td; char oid[MAX_OID_SIZE]; int len; k1 = 0; do { k1++; /* create a string like "attribute.?1" */ if (attr_name[0] != 0) snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%u", attr_name, k1); else snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%u", k1); len = sizeof(value) - 1; result = asn1_read_value(asn1_struct, tmpbuffer1, value, &len); if (result == ASN1_ELEMENT_NOT_FOUND) { gnutls_assert(); break; } if (result != ASN1_VALUE_NOT_FOUND) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } /* Move to the attibute type and values */ /* Read the OID */ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3), tmpbuffer1); _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), ".type"); len = sizeof(oid) - 1; result = asn1_read_value(asn1_struct, tmpbuffer3, oid, &len); if (result == ASN1_ELEMENT_NOT_FOUND) break; else if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } if (strcmp(oid, given_oid) == 0) { /* Found the OID */ /* Read the Value */ snprintf(tmpbuffer3, sizeof(tmpbuffer3), "%s.values.?%u", tmpbuffer1, indx + 1); len = sizeof(value) - 1; result = _gnutls_x509_read_value(asn1_struct, tmpbuffer3, &td); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } if (raw == 0) { result = _gnutls_x509_dn_to_string (oid, td.data, td.size, out); _gnutls_free_datum(&td); if (result < 0) { gnutls_assert(); goto cleanup; } return 0; } else { /* raw!=0 */ out->data = td.data; out->size = td.size; return 0; } } } while (1); gnutls_assert(); result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; cleanup: return result; } /** * gnutls_x509_crq_get_challenge_password: * @crq: should contain a #gnutls_x509_crq_t type * @pass: will hold a (0)-terminated password string * @pass_size: Initially holds the size of @pass. * * This function will return the challenge password in the request. * The challenge password is intended to be used for requesting a * revocation of the certificate. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_get_challenge_password(gnutls_x509_crq_t crq, char *pass, size_t * pass_size) { gnutls_datum_t td; int ret; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = parse_attribute(crq->crq, "certificationRequestInfo.attributes", "1.2.840.113549.1.9.7", 0, 0, &td); if (ret < 0) return gnutls_assert_val(ret); return _gnutls_strdatum_to_buf(&td, pass, pass_size); } /* This function will attempt to set the requested attribute in * the given X509v3 certificate. * * Critical will be either 0 or 1. */ static int add_attribute(ASN1_TYPE asn, const char *root, const char *attribute_id, const gnutls_datum_t * ext_data) { int result; char name[ASN1_MAX_NAME_SIZE]; snprintf(name, sizeof(name), "%s", root); /* Add a new attribute in the list. */ result = asn1_write_value(asn, name, "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } snprintf(name, sizeof(name), "%s.?LAST.type", root); result = asn1_write_value(asn, name, attribute_id, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } snprintf(name, sizeof(name), "%s.?LAST.values", root); result = asn1_write_value(asn, name, "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } snprintf(name, sizeof(name), "%s.?LAST.values.?LAST", root); result = _gnutls_x509_write_value(asn, name, ext_data); if (result < 0) { gnutls_assert(); return result; } return 0; } /* Overwrite the given attribute (using the index) * index here starts from one. */ static int overwrite_attribute(ASN1_TYPE asn, const char *root, unsigned int indx, const gnutls_datum_t * ext_data) { char name[ASN1_MAX_NAME_SIZE], name2[ASN1_MAX_NAME_SIZE]; int result; snprintf(name, sizeof(name), "%s.?%u", root, indx); _gnutls_str_cpy(name2, sizeof(name2), name); _gnutls_str_cat(name2, sizeof(name2), ".values.?LAST"); result = _gnutls_x509_write_value(asn, name2, ext_data); if (result < 0) { gnutls_assert(); return result; } return 0; } static int set_attribute(ASN1_TYPE asn, const char *root, const char *ext_id, const gnutls_datum_t * ext_data) { int result; int k, len; char name[ASN1_MAX_NAME_SIZE], name2[ASN1_MAX_NAME_SIZE]; char extnID[MAX_OID_SIZE]; /* Find the index of the given attribute. */ k = 0; do { k++; snprintf(name, sizeof(name), "%s.?%u", root, k); len = sizeof(extnID) - 1; result = asn1_read_value(asn, name, extnID, &len); /* move to next */ if (result == ASN1_ELEMENT_NOT_FOUND) { break; } do { _gnutls_str_cpy(name2, sizeof(name2), name); _gnutls_str_cat(name2, sizeof(name2), ".type"); len = sizeof(extnID) - 1; result = asn1_read_value(asn, name2, extnID, &len); if (result == ASN1_ELEMENT_NOT_FOUND) { gnutls_assert(); break; } else if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } /* Handle Extension */ if (strcmp(extnID, ext_id) == 0) { /* attribute was found */ return overwrite_attribute(asn, root, k, ext_data); } } while (0); } while (1); if (result == ASN1_ELEMENT_NOT_FOUND) { return add_attribute(asn, root, ext_id, ext_data); } else { gnutls_assert(); return _gnutls_asn2err(result); } return 0; } /** * gnutls_x509_crq_set_attribute_by_oid: * @crq: should contain a #gnutls_x509_crq_t type * @oid: holds an Object Identifier in a null-terminated string * @buf: a pointer to a structure that holds the attribute data * @buf_size: holds the size of @buf * * This function will set the attribute in the certificate request * specified by the given Object ID. The provided attribute must be be DER * encoded. * * Attributes in a certificate request is an optional set of data * appended to the request. Their interpretation depends on the CA policy. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_set_attribute_by_oid(gnutls_x509_crq_t crq, const char *oid, void *buf, size_t buf_size) { gnutls_datum_t data; data.data = buf; data.size = buf_size; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return set_attribute(crq->crq, "certificationRequestInfo.attributes", oid, &data); } /** * gnutls_x509_crq_get_attribute_by_oid: * @crq: should contain a #gnutls_x509_crq_t type * @oid: holds an Object Identifier in null-terminated string * @indx: In case multiple same OIDs exist in the attribute list, this * specifies which to get, use (0) to get the first one * @buf: a pointer to a structure to hold the attribute data (may be %NULL) * @buf_size: initially holds the size of @buf * * This function will return the attribute in the certificate request * specified by the given Object ID. The attribute will be DER * encoded. * * Attributes in a certificate request is an optional set of data * appended to the request. Their interpretation depends on the CA policy. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_get_attribute_by_oid(gnutls_x509_crq_t crq, const char *oid, int indx, void *buf, size_t * buf_size) { int ret; gnutls_datum_t td; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = parse_attribute(crq->crq, "certificationRequestInfo.attributes", oid, indx, 1, &td); if (ret < 0) return gnutls_assert_val(ret); return _gnutls_strdatum_to_buf(&td, buf, buf_size); } /** * gnutls_x509_crq_set_dn_by_oid: * @crq: should contain a #gnutls_x509_crq_t type * @oid: holds an Object Identifier in a (0)-terminated string * @raw_flag: must be 0, or 1 if the data are DER encoded * @data: a pointer to the input data * @sizeof_data: holds the size of @data * * This function will set the part of the name of the Certificate * request subject, specified by the given OID. The input string * should be ASCII or UTF-8 encoded. * * Some helper macros with popular OIDs can be found in gnutls/x509.h * With this function you can only set the known OIDs. You can test * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are * not known (by gnutls) you should properly DER encode your data, and * call this function with raw_flag set. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_set_dn_by_oid(gnutls_x509_crq_t crq, const char *oid, unsigned int raw_flag, const void *data, unsigned int sizeof_data) { if (sizeof_data == 0 || data == NULL || crq == NULL) { return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_set_dn_oid(crq->crq, "certificationRequestInfo.subject", oid, raw_flag, data, sizeof_data); } /** * gnutls_x509_crq_set_version: * @crq: should contain a #gnutls_x509_crq_t type * @version: holds the version number, for v1 Requests must be 1 * * This function will set the version of the certificate request. For * version 1 requests this must be one. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_set_version(gnutls_x509_crq_t crq, unsigned int version) { int result; unsigned char null = version; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (null > 0) null--; result = asn1_write_value(crq->crq, "certificationRequestInfo.version", &null, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } return 0; } /** * gnutls_x509_crq_get_version: * @crq: should contain a #gnutls_x509_crq_t type * * This function will return the version of the specified Certificate * request. * * Returns: version of certificate request, or a negative error code on * error. **/ int gnutls_x509_crq_get_version(gnutls_x509_crq_t crq) { uint8_t version[8]; int len, result; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } len = sizeof(version); if ((result = asn1_read_value(crq->crq, "certificationRequestInfo.version", version, &len)) != ASN1_SUCCESS) { if (result == ASN1_ELEMENT_NOT_FOUND) return 1; /* the DEFAULT version */ gnutls_assert(); return _gnutls_asn2err(result); } return (int) version[0] + 1; } /** * gnutls_x509_crq_set_key: * @crq: should contain a #gnutls_x509_crq_t type * @key: holds a private key * * This function will set the public parameters from the given private * key to the request. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_set_key(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) { int result; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = _gnutls_x509_encode_and_copy_PKI_params (crq->crq, "certificationRequestInfo.subjectPKInfo", key->pk_algorithm, &key->params); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_get_key_rsa_raw: * @crq: Holds the certificate * @m: will hold the modulus * @e: will hold the public exponent * * This function will export the RSA public key's parameters found in * the given structure. The new parameters will be allocated using * gnutls_malloc() and will be stored in the appropriate datum. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_key_rsa_raw(gnutls_x509_crq_t crq, gnutls_datum_t * m, gnutls_datum_t * e) { int ret; gnutls_pk_params_st params; gnutls_pk_params_init(¶ms); if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL); if (ret != GNUTLS_PK_RSA) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_mpi_dprint(params.params[0], m); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_mpi_dprint(params.params[1], e); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(m); goto cleanup; } ret = 0; cleanup: gnutls_pk_params_release(¶ms); return ret; } /** * gnutls_x509_crq_set_key_rsa_raw: * @crq: should contain a #gnutls_x509_crq_t type * @m: holds the modulus * @e: holds the public exponent * * This function will set the public parameters from the given private * key to the request. Only RSA keys are currently supported. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.6.0 **/ int gnutls_x509_crq_set_key_rsa_raw(gnutls_x509_crq_t crq, const gnutls_datum_t * m, const gnutls_datum_t * e) { int result, ret; size_t siz = 0; gnutls_pk_params_st temp_params; gnutls_pk_params_init(&temp_params); if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } memset(&temp_params, 0, sizeof(temp_params)); siz = m->size; if (_gnutls_mpi_init_scan_nz(&temp_params.params[0], m->data, siz)) { gnutls_assert(); ret = GNUTLS_E_MPI_SCAN_FAILED; goto error; } siz = e->size; if (_gnutls_mpi_init_scan_nz(&temp_params.params[1], e->data, siz)) { gnutls_assert(); ret = GNUTLS_E_MPI_SCAN_FAILED; goto error; } temp_params.params_nr = RSA_PUBLIC_PARAMS; result = _gnutls_x509_encode_and_copy_PKI_params (crq->crq, "certificationRequestInfo.subjectPKInfo", GNUTLS_PK_RSA, &temp_params); if (result < 0) { gnutls_assert(); ret = result; goto error; } ret = 0; error: gnutls_pk_params_release(&temp_params); return ret; } /** * gnutls_x509_crq_set_challenge_password: * @crq: should contain a #gnutls_x509_crq_t type * @pass: holds a (0)-terminated password * * This function will set a challenge password to be used when * revoking the request. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_set_challenge_password(gnutls_x509_crq_t crq, const char *pass) { int result; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* Add the attribute. */ result = asn1_write_value(crq->crq, "certificationRequestInfo.attributes", "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = _gnutls_x509_encode_and_write_attribute ("1.2.840.113549.1.9.7", crq->crq, "certificationRequestInfo.attributes.?LAST", pass, strlen(pass), 1); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_sign2: * @crq: should contain a #gnutls_x509_crq_t type * @key: holds a private key * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA1 * @flags: must be 0 * * This function will sign the certificate request with a private key. * This must be the same key as the one used in * gnutls_x509_crt_set_key() since a certificate request is self * signed. * * This must be the last step in a certificate request generation * since all the previously set parameters are now signed. * * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all * information in the certificate request (e.g., the version using * gnutls_x509_crq_set_version()). * **/ int gnutls_x509_crq_sign2(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key, gnutls_digest_algorithm_t dig, unsigned int flags) { int result; gnutls_privkey_t privkey; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = gnutls_privkey_init(&privkey); if (result < 0) { gnutls_assert(); return result; } result = gnutls_privkey_import_x509(privkey, key, 0); if (result < 0) { gnutls_assert(); goto fail; } result = gnutls_x509_crq_privkey_sign(crq, privkey, dig, flags); if (result < 0) { gnutls_assert(); goto fail; } result = 0; fail: gnutls_privkey_deinit(privkey); return result; } /** * gnutls_x509_crq_sign: * @crq: should contain a #gnutls_x509_crq_t type * @key: holds a private key * * This function is the same a gnutls_x509_crq_sign2() with no flags, * and SHA1 as the hash algorithm. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Deprecated: Use gnutls_x509_crq_privkey_sign() instead. */ int gnutls_x509_crq_sign(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) { return gnutls_x509_crq_sign2(crq, key, GNUTLS_DIG_SHA1, 0); } /** * gnutls_x509_crq_export: * @crq: should contain a #gnutls_x509_crq_t type * @format: the format of output params. One of PEM or DER. * @output_data: will contain a certificate request PEM or DER encoded * @output_data_size: holds the size of output_data (and will be * replaced by the actual size of parameters) * * This function will export the certificate request to a PEM or DER * encoded PKCS10 structure. * * If the buffer provided is not long enough to hold the output, then * %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned and * *@output_data_size will be updated. * * If the structure is PEM encoded, it will have a header of "BEGIN * NEW CERTIFICATE REQUEST". * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_export(gnutls_x509_crq_t crq, gnutls_x509_crt_fmt_t format, void *output_data, size_t * output_data_size) { if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_export_int(crq->crq, format, PEM_CRQ, output_data, output_data_size); } /** * gnutls_x509_crq_export2: * @crq: should contain a #gnutls_x509_crq_t type * @format: the format of output params. One of PEM or DER. * @out: will contain a certificate request PEM or DER encoded * * This function will export the certificate request to a PEM or DER * encoded PKCS10 structure. * * The output buffer is allocated using gnutls_malloc(). * * If the structure is PEM encoded, it will have a header of "BEGIN * NEW CERTIFICATE REQUEST". * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since 3.1.3 **/ int gnutls_x509_crq_export2(gnutls_x509_crq_t crq, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) { if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return _gnutls_x509_export_int2(crq->crq, format, PEM_CRQ, out); } /** * gnutls_x509_crq_get_pk_algorithm: * @crq: should contain a #gnutls_x509_crq_t type * @bits: if bits is non-%NULL it will hold the size of the parameters' in bits * * This function will return the public key algorithm of a PKCS#10 * certificate request. * * If bits is non-%NULL, it should have enough size to hold the * parameters size in bits. For RSA the bits returned is the modulus. * For DSA the bits returned are of the public exponent. * * Returns: a member of the #gnutls_pk_algorithm_t enumeration on * success, or a negative error code on error. **/ int gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits) { int result; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = _gnutls_x509_get_pk_algorithm (crq->crq, "certificationRequestInfo.subjectPKInfo", bits); if (result < 0) { gnutls_assert(); } return result; } /** * gnutls_x509_crq_get_attribute_info: * @crq: should contain a #gnutls_x509_crq_t type * @indx: Specifies which attribute number to get. Use (0) to get the first one. * @oid: a pointer to a structure to hold the OID * @sizeof_oid: initially holds the maximum size of @oid, on return * holds actual size of @oid. * * This function will return the requested attribute OID in the * certificate, and the critical flag for it. The attribute OID will * be stored as a string in the provided buffer. Use * gnutls_x509_crq_get_attribute_data() to extract the data. * * If the buffer provided is not long enough to hold the output, then * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be * returned. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error code in case of an error. If your have reached the * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE * will be returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_attribute_info(gnutls_x509_crq_t crq, int indx, void *oid, size_t * sizeof_oid) { int result; char name[ASN1_MAX_NAME_SIZE]; int len; if (!crq) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } snprintf(name, sizeof(name), "certificationRequestInfo.attributes.?%u.type", indx + 1); len = *sizeof_oid; result = asn1_read_value(crq->crq, name, oid, &len); *sizeof_oid = len; if (result == ASN1_ELEMENT_NOT_FOUND) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; else if (result < 0) { gnutls_assert(); return _gnutls_asn2err(result); } return 0; } /** * gnutls_x509_crq_get_attribute_data: * @crq: should contain a #gnutls_x509_crq_t type * @indx: Specifies which attribute number to get. Use (0) to get the first one. * @data: a pointer to a structure to hold the data (may be null) * @sizeof_data: initially holds the size of @oid * * This function will return the requested attribute data in the * certificate request. The attribute data will be stored as a string in the * provided buffer. * * Use gnutls_x509_crq_get_attribute_info() to extract the OID. * Use gnutls_x509_crq_get_attribute_by_oid() instead, * if you want to get data indexed by the attribute OID rather than * sequence. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error code in case of an error. If your have reached the * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE * will be returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_attribute_data(gnutls_x509_crq_t crq, int indx, void *data, size_t * sizeof_data) { int result, len; char name[ASN1_MAX_NAME_SIZE]; if (!crq) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } snprintf(name, sizeof(name), "certificationRequestInfo.attributes.?%u.values.?1", indx + 1); len = *sizeof_data; result = asn1_read_value(crq->crq, name, data, &len); *sizeof_data = len; if (result == ASN1_ELEMENT_NOT_FOUND) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; else if (result < 0) { gnutls_assert(); return _gnutls_asn2err(result); } return 0; } /** * gnutls_x509_crq_get_extension_info: * @crq: should contain a #gnutls_x509_crq_t type * @indx: Specifies which extension number to get. Use (0) to get the first one. * @oid: a pointer to store the OID * @sizeof_oid: initially holds the maximum size of @oid, on return * holds actual size of @oid. * @critical: output variable with critical flag, may be NULL. * * This function will return the requested extension OID in the * certificate, and the critical flag for it. The extension OID will * be stored as a string in the provided buffer. Use * gnutls_x509_crq_get_extension_data() to extract the data. * * If the buffer provided is not long enough to hold the output, then * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be * returned. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error code in case of an error. If your have reached the * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE * will be returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_extension_info(gnutls_x509_crq_t crq, int indx, void *oid, size_t * sizeof_oid, unsigned int *critical) { int result; char str_critical[10]; char name[ASN1_MAX_NAME_SIZE]; char *extensions = NULL; size_t extensions_size = 0; ASN1_TYPE c2; int len; if (!crq) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* read extensionRequest */ result = gnutls_x509_crq_get_attribute_by_oid(crq, "1.2.840.113549.1.9.14", 0, NULL, &extensions_size); if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) { extensions = gnutls_malloc(extensions_size); if (extensions == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = gnutls_x509_crq_get_attribute_by_oid(crq, "1.2.840.113549.1.9.14", 0, extensions, &extensions_size); } if (result < 0) { gnutls_assert(); goto out; } result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto out; } result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&c2); result = _gnutls_asn2err(result); goto out; } snprintf(name, sizeof(name), "?%u.extnID", indx + 1); len = *sizeof_oid; result = asn1_read_value(c2, name, oid, &len); *sizeof_oid = len; if (result == ASN1_ELEMENT_NOT_FOUND) { asn1_delete_structure(&c2); result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; goto out; } else if (result < 0) { gnutls_assert(); asn1_delete_structure(&c2); result = _gnutls_asn2err(result); goto out; } snprintf(name, sizeof(name), "?%u.critical", indx + 1); len = sizeof(str_critical); result = asn1_read_value(c2, name, str_critical, &len); asn1_delete_structure(&c2); if (result < 0) { gnutls_assert(); result = _gnutls_asn2err(result); goto out; } if (critical) { if (str_critical[0] == 'T') *critical = 1; else *critical = 0; } result = 0; out: gnutls_free(extensions); return result; } /** * gnutls_x509_crq_get_extension_data: * @crq: should contain a #gnutls_x509_crq_t type * @indx: Specifies which extension number to get. Use (0) to get the first one. * @data: a pointer to a structure to hold the data (may be null) * @sizeof_data: initially holds the size of @oid * * This function will return the requested extension data in the * certificate. The extension data will be stored as a string in the * provided buffer. * * Use gnutls_x509_crq_get_extension_info() to extract the OID and * critical flag. Use gnutls_x509_crq_get_extension_by_oid() instead, * if you want to get data indexed by the extension OID rather than * sequence. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error code in case of an error. If your have reached the * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE * will be returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_extension_data(gnutls_x509_crq_t crq, int indx, void *data, size_t * sizeof_data) { int ret; gnutls_datum_t raw; ret = gnutls_x509_crq_get_extension_data2(crq, indx, &raw); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_copy_data(&raw, data, sizeof_data); if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && data == NULL) ret = 0; gnutls_free(raw.data); return ret; } /** * gnutls_x509_crq_get_extension_data2: * @crq: should contain a #gnutls_x509_crq_t type * @extension_id: An X.509 extension OID. * @indx: Specifies which extension OID to read. Use (0) to get the first one. * @data: will contain the extension DER-encoded data * * This function will return the requested extension data in the * certificate request. The extension data will be allocated using * gnutls_malloc(). * * Use gnutls_x509_crq_get_extension_info() to extract the OID. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, * otherwise a negative error code is returned. If you have reached the * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE * will be returned. * * Since: 3.3.0 **/ int gnutls_x509_crq_get_extension_data2(gnutls_x509_crq_t crq, unsigned indx, gnutls_datum_t * data) { int ret, result; char name[ASN1_MAX_NAME_SIZE]; unsigned char *extensions = NULL; size_t extensions_size = 0; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; if (!crq) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* read extensionRequest */ ret = gnutls_x509_crq_get_attribute_by_oid(crq, "1.2.840.113549.1.9.14", 0, NULL, &extensions_size); if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { gnutls_assert(); if (ret == 0) return GNUTLS_E_INTERNAL_ERROR; return ret; } extensions = gnutls_malloc(extensions_size); if (extensions == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } ret = gnutls_x509_crq_get_attribute_by_oid(crq, "1.2.840.113549.1.9.14", 0, extensions, &extensions_size); if (ret < 0) { gnutls_assert(); goto cleanup; } result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); ret = _gnutls_asn2err(result); goto cleanup; } result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); ret = _gnutls_asn2err(result); goto cleanup; } snprintf(name, sizeof(name), "?%u.extnValue", indx + 1); ret = _gnutls_x509_read_value(c2, name, data); if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; goto cleanup; } else if (ret < 0) { gnutls_assert(); goto cleanup; } ret = 0; cleanup: asn1_delete_structure(&c2); gnutls_free(extensions); return ret; } /** * gnutls_x509_crq_get_key_usage: * @crq: should contain a #gnutls_x509_crq_t type * @key_usage: where the key usage bits will be stored * @critical: will be non-zero if the extension is marked as critical * * This function will return certificate's key usage, by reading the * keyUsage X.509 extension (2.5.29.15). The key usage value will * ORed values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE, * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT, * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT, * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN, * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY. * * Returns: the certificate key usage, or a negative error code in case of * parsing error. If the certificate does not contain the keyUsage * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be * returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_key_usage(gnutls_x509_crq_t crq, unsigned int *key_usage, unsigned int *critical) { int result; uint8_t buf[128]; size_t buf_size = sizeof(buf); gnutls_datum_t bd; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.15", 0, buf, &buf_size, critical); if (result < 0) { gnutls_assert(); return result; } bd.data = buf; bd.size = buf_size; result = gnutls_x509_ext_import_key_usage(&bd, key_usage); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_get_basic_constraints: * @crq: should contain a #gnutls_x509_crq_t type * @critical: will be non-zero if the extension is marked as critical * @ca: pointer to output integer indicating CA status, may be NULL, * value is 1 if the certificate CA flag is set, 0 otherwise. * @pathlen: pointer to output integer indicating path length (may be * NULL), non-negative error codes indicate a present pathLenConstraint * field and the actual value, -1 indicate that the field is absent. * * This function will read the certificate's basic constraints, and * return the certificates CA status. It reads the basicConstraints * X.509 extension (2.5.29.19). * * Returns: If the certificate is a CA a positive value will be * returned, or (0) if the certificate does not have CA flag set. * A negative error code may be returned in case of errors. If the * certificate does not contain the basicConstraints extension * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_basic_constraints(gnutls_x509_crq_t crq, unsigned int *critical, unsigned int *ca, int *pathlen) { int result; unsigned int tmp_ca; uint8_t buf[256]; size_t buf_size = sizeof(buf); gnutls_datum_t bd; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.19", 0, buf, &buf_size, critical); if (result < 0) { gnutls_assert(); return result; } bd.data = buf; bd.size = buf_size; result = gnutls_x509_ext_import_basic_constraints(&bd, &tmp_ca, pathlen); if (ca) *ca = tmp_ca; if (result < 0) { gnutls_assert(); return result; } return tmp_ca; } static int get_subject_alt_name(gnutls_x509_crq_t crq, unsigned int seq, void *ret, size_t * ret_size, unsigned int *ret_type, unsigned int *critical, int othername_oid) { int result; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; gnutls_x509_subject_alt_name_t type; gnutls_datum_t dnsname = { NULL, 0 }; size_t dns_size = 0; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (ret) memset(ret, 0, *ret_size); else *ret_size = 0; /* Extract extension. */ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, NULL, &dns_size, critical); if (result < 0) { gnutls_assert(); return result; } dnsname.size = dns_size; dnsname.data = gnutls_malloc(dnsname.size); if (dnsname.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, dnsname.data, &dns_size, critical); if (result < 0) { gnutls_assert(); gnutls_free(dnsname.data); return result; } result = asn1_create_element (_gnutls_get_pkix(), "PKIX1.SubjectAltName", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); gnutls_free(dnsname.data); return _gnutls_asn2err(result); } result = _asn1_strict_der_decode(&c2, dnsname.data, dnsname.size, NULL); gnutls_free(dnsname.data); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&c2); return _gnutls_asn2err(result); } result = _gnutls_parse_general_name(c2, "", seq, ret, ret_size, ret_type, othername_oid); asn1_delete_structure(&c2); if (result < 0) { return result; } type = result; return type; } /** * gnutls_x509_crq_get_subject_alt_name: * @crq: should contain a #gnutls_x509_crq_t type * @seq: specifies the sequence number of the alt name, 0 for the * first one, 1 for the second etc. * @ret: is the place where the alternative name will be copied to * @ret_size: holds the size of ret. * @ret_type: holds the #gnutls_x509_subject_alt_name_t name type * @critical: will be non-zero if the extension is marked as critical * (may be null) * * This function will return the alternative names, contained in the * given certificate. It is the same as * gnutls_x509_crq_get_subject_alt_name() except for the fact that it * will return the type of the alternative name in @ret_type even if * the function fails for some reason (i.e. the buffer provided is * not enough). * * Returns: the alternative subject name type on success, one of the * enumerated #gnutls_x509_subject_alt_name_t. It will return * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough to * hold the value. In that case @ret_size will be updated with the * required size. If the certificate request does not have an * Alternative name with the specified sequence number then * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_subject_alt_name(gnutls_x509_crq_t crq, unsigned int seq, void *ret, size_t * ret_size, unsigned int *ret_type, unsigned int *critical) { return get_subject_alt_name(crq, seq, ret, ret_size, ret_type, critical, 0); } /** * gnutls_x509_crq_get_subject_alt_othername_oid: * @crq: should contain a #gnutls_x509_crq_t type * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) * @ret: is the place where the otherName OID will be copied to * @ret_size: holds the size of ret. * * This function will extract the type OID of an otherName Subject * Alternative Name, contained in the given certificate, and return * the type as an enumerated element. * * This function is only useful if * gnutls_x509_crq_get_subject_alt_name() returned * %GNUTLS_SAN_OTHERNAME. * * Returns: the alternative subject name type on success, one of the * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, * it will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if * @ret_size is not large enough to hold the value. In that case * @ret_size will be updated with the required size. If the * certificate does not have an Alternative name with the specified * sequence number and with the otherName type then * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_subject_alt_othername_oid(gnutls_x509_crq_t crq, unsigned int seq, void *ret, size_t * ret_size) { return get_subject_alt_name(crq, seq, ret, ret_size, NULL, NULL, 1); } /** * gnutls_x509_crq_get_extension_by_oid: * @crq: should contain a #gnutls_x509_crq_t type * @oid: holds an Object Identifier in a null terminated string * @indx: In case multiple same OIDs exist in the extensions, this * specifies which to get. Use (0) to get the first one. * @buf: a pointer to a structure to hold the name (may be null) * @buf_size: initially holds the size of @buf * @critical: will be non-zero if the extension is marked as critical * * This function will return the extension specified by the OID in * the certificate. The extensions will be returned as binary data * DER encoded, in the provided buffer. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error code in case of an error. If the certificate does not * contain the specified extension * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_extension_by_oid(gnutls_x509_crq_t crq, const char *oid, int indx, void *buf, size_t * buf_size, unsigned int *critical) { int result; unsigned int i; char _oid[MAX_OID_SIZE]; size_t oid_size; for (i = 0;; i++) { oid_size = sizeof(_oid); result = gnutls_x509_crq_get_extension_info(crq, i, _oid, &oid_size, critical); if (result < 0) { gnutls_assert(); return result; } if (strcmp(oid, _oid) == 0) { /* found */ if (indx == 0) return gnutls_x509_crq_get_extension_data(crq, i, buf, buf_size); else indx--; } } return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } /** * gnutls_x509_crq_get_extension_by_oid2: * @crq: should contain a #gnutls_x509_crq_t type * @oid: holds an Object Identifier in a null terminated string * @indx: In case multiple same OIDs exist in the extensions, this * specifies which to get. Use (0) to get the first one. * @output: will hold the allocated extension data * @critical: will be non-zero if the extension is marked as critical * * This function will return the extension specified by the OID in * the certificate. The extensions will be returned as binary data * DER encoded, in the provided buffer. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error code in case of an error. If the certificate does not * contain the specified extension * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. * * Since: 3.3.8 **/ int gnutls_x509_crq_get_extension_by_oid2(gnutls_x509_crq_t crq, const char *oid, int indx, gnutls_datum_t *output, unsigned int *critical) { int result; unsigned int i; char _oid[MAX_OID_SIZE]; size_t oid_size; for (i = 0;; i++) { oid_size = sizeof(_oid); result = gnutls_x509_crq_get_extension_info(crq, i, _oid, &oid_size, critical); if (result < 0) { gnutls_assert(); return result; } if (strcmp(oid, _oid) == 0) { /* found */ if (indx == 0) return gnutls_x509_crq_get_extension_data2(crq, i, output); else indx--; } } return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } /** * gnutls_x509_crq_set_subject_alt_name: * @crq: a certificate request of type #gnutls_x509_crq_t * @nt: is one of the #gnutls_x509_subject_alt_name_t enumerations * @data: The data to be set * @data_size: The size of data to be set * @flags: %GNUTLS_FSAN_SET to clear previous data or * %GNUTLS_FSAN_APPEND to append. * * This function will set the subject alternative name certificate * extension. It can set the following types: * * %GNUTLS_SAN_DNSNAME: as a text string * * %GNUTLS_SAN_RFC822NAME: as a text string * * %GNUTLS_SAN_URI: as a text string * * %GNUTLS_SAN_IPADDRESS: as a binary IP address (4 or 16 bytes) * * Other values can be set as binary values with the proper DER encoding. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.8.0 **/ int gnutls_x509_crq_set_subject_alt_name(gnutls_x509_crq_t crq, gnutls_x509_subject_alt_name_t nt, const void *data, unsigned int data_size, unsigned int flags) { int result = 0; gnutls_datum_t der_data = { NULL, 0 }; gnutls_datum_t prev_der_data = { NULL, 0 }; unsigned int critical = 0; size_t prev_data_size = 0; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* Check if the extension already exists. */ if (flags == GNUTLS_FSAN_APPEND) { result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, NULL, &prev_data_size, &critical); prev_der_data.size = prev_data_size; switch (result) { case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: /* Replacing non-existing data means the same as set data. */ break; case GNUTLS_E_SUCCESS: prev_der_data.data = gnutls_malloc(prev_der_data.size); if (prev_der_data.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, prev_der_data. data, &prev_data_size, &critical); if (result < 0) { gnutls_assert(); gnutls_free(prev_der_data.data); return result; } break; default: gnutls_assert(); return result; } } /* generate the extension. */ result = _gnutls_x509_ext_gen_subject_alt_name(nt, data, data_size, &prev_der_data, &der_data); gnutls_free(prev_der_data.data); if (result < 0) { gnutls_assert(); goto finish; } result = _gnutls_x509_crq_set_extension(crq, "2.5.29.17", &der_data, critical); _gnutls_free_datum(&der_data); if (result < 0) { gnutls_assert(); return result; } return 0; finish: return result; } /** * gnutls_x509_crq_set_basic_constraints: * @crq: a certificate request of type #gnutls_x509_crq_t * @ca: true(1) or false(0) depending on the Certificate authority status. * @pathLenConstraint: non-negative error codes indicate maximum length of path, * and negative error codes indicate that the pathLenConstraints field should * not be present. * * This function will set the basicConstraints certificate extension. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.8.0 **/ int gnutls_x509_crq_set_basic_constraints(gnutls_x509_crq_t crq, unsigned int ca, int pathLenConstraint) { int result; gnutls_datum_t der_data; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* generate the extension. */ result = gnutls_x509_ext_export_basic_constraints(ca, pathLenConstraint, &der_data); if (result < 0) { gnutls_assert(); return result; } result = _gnutls_x509_crq_set_extension(crq, "2.5.29.19", &der_data, 1); _gnutls_free_datum(&der_data); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_set_key_usage: * @crq: a certificate request of type #gnutls_x509_crq_t * @usage: an ORed sequence of the GNUTLS_KEY_* elements. * * This function will set the keyUsage certificate extension. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.8.0 **/ int gnutls_x509_crq_set_key_usage(gnutls_x509_crq_t crq, unsigned int usage) { int result; gnutls_datum_t der_data; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* generate the extension. */ result = gnutls_x509_ext_export_key_usage(usage, &der_data); if (result < 0) { gnutls_assert(); return result; } result = _gnutls_x509_crq_set_extension(crq, "2.5.29.15", &der_data, 1); _gnutls_free_datum(&der_data); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_get_key_purpose_oid: * @crq: should contain a #gnutls_x509_crq_t type * @indx: This specifies which OID to return, use (0) to get the first one * @oid: a pointer to store the OID (may be %NULL) * @sizeof_oid: initially holds the size of @oid * @critical: output variable with critical flag, may be %NULL. * * This function will extract the key purpose OIDs of the Certificate * specified by the given index. These are stored in the Extended Key * Usage extension (2.5.29.37). See the GNUTLS_KP_* definitions for * human readable names. * * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is * not long enough, and in that case the *@sizeof_oid will be * updated with the required size. On success 0 is returned. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_key_purpose_oid(gnutls_x509_crq_t crq, int indx, void *oid, size_t * sizeof_oid, unsigned int *critical) { char tmpstr[ASN1_MAX_NAME_SIZE]; int result, len; gnutls_datum_t prev = { NULL, 0 }; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; size_t prev_size = 0; if (oid) memset(oid, 0, *sizeof_oid); else *sizeof_oid = 0; /* Extract extension. */ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, NULL, &prev_size, critical); prev.size = prev_size; if (result < 0) { gnutls_assert(); return result; } prev.data = gnutls_malloc(prev.size); if (prev.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, prev.data, &prev_size, critical); if (result < 0) { gnutls_assert(); gnutls_free(prev.data); return result; } result = asn1_create_element (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); gnutls_free(prev.data); return _gnutls_asn2err(result); } result = _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL); gnutls_free(prev.data); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&c2); return _gnutls_asn2err(result); } indx++; /* create a string like "?1" */ snprintf(tmpstr, sizeof(tmpstr), "?%u", indx); len = *sizeof_oid; result = asn1_read_value(c2, tmpstr, oid, &len); *sizeof_oid = len; asn1_delete_structure(&c2); if (result == ASN1_VALUE_NOT_FOUND || result == ASN1_ELEMENT_NOT_FOUND) { return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } if (result != ASN1_SUCCESS) { if (result != ASN1_MEM_ERROR) gnutls_assert(); return _gnutls_asn2err(result); } return 0; } /** * gnutls_x509_crq_set_key_purpose_oid: * @crq: a certificate of type #gnutls_x509_crq_t * @oid: a pointer to a null-terminated string that holds the OID * @critical: Whether this extension will be critical or not * * This function will set the key purpose OIDs of the Certificate. * These are stored in the Extended Key Usage extension (2.5.29.37) * See the GNUTLS_KP_* definitions for human readable names. * * Subsequent calls to this function will append OIDs to the OID list. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.8.0 **/ int gnutls_x509_crq_set_key_purpose_oid(gnutls_x509_crq_t crq, const void *oid, unsigned int critical) { int result; gnutls_datum_t prev = { NULL, 0 }, der_data; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; size_t prev_size = 0; /* Read existing extension, if there is one. */ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, NULL, &prev_size, &critical); prev.size = prev_size; switch (result) { case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: /* No existing extension, that's fine. */ break; case GNUTLS_E_SUCCESS: prev.data = gnutls_malloc(prev.size); if (prev.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, prev.data, &prev_size, &critical); if (result < 0) { gnutls_assert(); gnutls_free(prev.data); return result; } break; default: gnutls_assert(); return result; } result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); gnutls_free(prev.data); return _gnutls_asn2err(result); } if (prev.data) { /* decode it. */ result = _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL); gnutls_free(prev.data); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&c2); return _gnutls_asn2err(result); } } /* generate the extension. */ /* 1. create a new element. */ result = asn1_write_value(c2, "", "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&c2); return _gnutls_asn2err(result); } /* 2. Add the OID. */ result = asn1_write_value(c2, "?LAST", oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&c2); return _gnutls_asn2err(result); } result = _gnutls_x509_der_encode(c2, "", &der_data, 0); asn1_delete_structure(&c2); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = _gnutls_x509_crq_set_extension(crq, "2.5.29.37", &der_data, critical); _gnutls_free_datum(&der_data); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_get_key_id: * @crq: a certificate of type #gnutls_x509_crq_t * @flags: should be one of the flags from %gnutls_keyid_flags_t * @output_data: will contain the key ID * @output_data_size: holds the size of output_data (and will be * replaced by the actual size of parameters) * * This function will return a unique ID that depends on the public key * parameters. This ID can be used in checking whether a certificate * corresponds to the given private key. * * If the buffer provided is not long enough to hold the output, then * *@output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will * be returned. The output will normally be a SHA-1 hash output, * which is 20 bytes. * * Returns: In case of failure a negative error code will be * returned, and 0 on success. * * Since: 2.8.0 **/ int gnutls_x509_crq_get_key_id(gnutls_x509_crq_t crq, unsigned int flags, unsigned char *output_data, size_t * output_data_size) { int pk, ret = 0; gnutls_pk_params_st params; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } pk = gnutls_x509_crq_get_pk_algorithm(crq, NULL); if (pk < 0) { gnutls_assert(); return pk; } ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_get_key_id(pk, ¶ms, output_data, output_data_size, flags); gnutls_pk_params_release(¶ms); return ret; } /** * gnutls_x509_crq_privkey_sign: * @crq: should contain a #gnutls_x509_crq_t type * @key: holds a private key * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA1 * @flags: must be 0 * * This function will sign the certificate request with a private key. * This must be the same key as the one used in * gnutls_x509_crt_set_key() since a certificate request is self * signed. * * This must be the last step in a certificate request generation * since all the previously set parameters are now signed. * * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all * information in the certificate request (e.g., the version using * gnutls_x509_crq_set_version()). * * Since: 2.12.0 **/ int gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, gnutls_digest_algorithm_t dig, unsigned int flags) { int result; gnutls_datum_t signature; gnutls_datum_t tbs; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* Make sure version field is set. */ if (gnutls_x509_crq_get_version(crq) == GNUTLS_E_ASN1_VALUE_NOT_FOUND) { result = gnutls_x509_crq_set_version(crq, 1); if (result < 0) { gnutls_assert(); return result; } } /* Step 1. Self sign the request. */ result = _gnutls_x509_get_tbs(crq->crq, "certificationRequestInfo", &tbs); if (result < 0) { gnutls_assert(); return result; } result = gnutls_privkey_sign_data(key, dig, 0, &tbs, &signature); gnutls_free(tbs.data); if (result < 0) { gnutls_assert(); return result; } /* Step 2. write the signature (bits) */ result = asn1_write_value(crq->crq, "signature", signature.data, signature.size * 8); _gnutls_free_datum(&signature); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } /* Step 3. Write the signatureAlgorithm field. */ result = _gnutls_x509_write_sig_params(crq->crq, "signatureAlgorithm", gnutls_privkey_get_pk_algorithm (key, NULL), dig); if (result < 0) { gnutls_assert(); return result; } return 0; } /** * gnutls_x509_crq_verify: * @crq: is the crq to be verified * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. * * This function will verify self signature in the certificate * request and return its status. * * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED * is returned, and zero or positive code on success. * * Since 2.12.0 **/ int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags) { gnutls_datum data = { NULL, 0 }; gnutls_datum signature = { NULL, 0 }; gnutls_pk_params_st params; gnutls_digest_algorithm_t algo; int ret; gnutls_pk_params_init(¶ms); ret = _gnutls_x509_get_signed_data(crq->crq, NULL, "certificationRequestInfo", &data); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_x509_get_signature_algorithm(crq->crq, "signatureAlgorithm.algorithm"); if (ret < 0) { gnutls_assert(); goto cleanup; } algo = gnutls_sign_get_hash_algorithm(ret); ret = _gnutls_x509_get_signature(crq->crq, "signature", &signature); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = pubkey_verify_data(gnutls_x509_crq_get_pk_algorithm(crq, NULL), hash_to_entry(algo), &data, &signature, ¶ms); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = 0; cleanup: _gnutls_free_datum(&data); _gnutls_free_datum(&signature); gnutls_pk_params_release(¶ms); return ret; } /** * gnutls_x509_crq_set_private_key_usage_period: * @crq: a certificate of type #gnutls_x509_crq_t * @activation: The activation time * @expiration: The expiration time * * This function will set the private key usage period extension (2.5.29.16). * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crq_set_private_key_usage_period(gnutls_x509_crq_t crq, time_t activation, time_t expiration) { int result; gnutls_datum_t der_data; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; if (crq == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = _gnutls_x509_set_time(c2, "notBefore", activation, 1); if (result < 0) { gnutls_assert(); goto cleanup; } result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1); if (result < 0) { gnutls_assert(); goto cleanup; } result = _gnutls_x509_der_encode(c2, "", &der_data, 0); if (result < 0) { gnutls_assert(); goto cleanup; } result = _gnutls_x509_crq_set_extension(crq, "2.5.29.16", &der_data, 0); _gnutls_free_datum(&der_data); cleanup: asn1_delete_structure(&c2); return result; }