summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/certhigh/certreq.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/certhigh/certreq.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/security/nss/lib/certhigh/certreq.c b/security/nss/lib/certhigh/certreq.c
new file mode 100644
index 0000000000..2ab4f1ab7b
--- /dev/null
+++ b/security/nss/lib/certhigh/certreq.c
@@ -0,0 +1,331 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cert.h"
+#include "certt.h"
+#include "secder.h"
+#include "keyhi.h"
+#include "secitem.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+
+const SEC_ASN1Template CERT_AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTAttribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(CERTAttribute, attrValue),
+ SEC_ASN1_SUB(SEC_AnyTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template CERT_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, CERT_AttributeTemplate },
+};
+
+const SEC_ASN1Template CERT_CertificateRequestTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(CERTCertificateRequest) },
+ { SEC_ASN1_INTEGER,
+ offsetof(CERTCertificateRequest, version) },
+ { SEC_ASN1_INLINE,
+ offsetof(CERTCertificateRequest, subject),
+ CERT_NameTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(CERTCertificateRequest, subjectPublicKeyInfo),
+ CERT_SubjectPublicKeyInfoTemplate },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(CERTCertificateRequest, attributes),
+ CERT_SetOfAttributeTemplate },
+ { 0 }
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateRequestTemplate)
+
+CERTCertificate *
+CERT_CreateCertificate(unsigned long serialNumber,
+ CERTName *issuer,
+ CERTValidity *validity,
+ CERTCertificateRequest *req)
+{
+ CERTCertificate *c;
+ int rv;
+ PLArenaPool *arena;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if (!arena) {
+ return (0);
+ }
+
+ c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
+
+ if (!c) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return 0;
+ }
+
+ c->referenceCount = 1;
+ c->arena = arena;
+
+ /*
+ * Default is a plain version 1.
+ * If extensions are added, it will get changed as appropriate.
+ */
+ rv = DER_SetUInteger(arena, &c->version, SEC_CERTIFICATE_VERSION_1);
+ if (rv)
+ goto loser;
+
+ rv = DER_SetUInteger(arena, &c->serialNumber, serialNumber);
+ if (rv)
+ goto loser;
+
+ rv = CERT_CopyName(arena, &c->issuer, issuer);
+ if (rv)
+ goto loser;
+
+ rv = CERT_CopyValidity(arena, &c->validity, validity);
+ if (rv)
+ goto loser;
+
+ rv = CERT_CopyName(arena, &c->subject, &req->subject);
+ if (rv)
+ goto loser;
+ rv = SECKEY_CopySubjectPublicKeyInfo(arena, &c->subjectPublicKeyInfo,
+ &req->subjectPublicKeyInfo);
+ if (rv)
+ goto loser;
+
+ return c;
+
+loser:
+ CERT_DestroyCertificate(c);
+ return 0;
+}
+
+/************************************************************************/
+/* It's clear from the comments that the original author of this
+ * function expected the template for certificate requests to treat
+ * the attributes as a SET OF ANY. This function expected to be
+ * passed an array of SECItems each of which contained an already encoded
+ * Attribute. But the cert request template does not treat the
+ * Attributes as a SET OF ANY, and AFAIK never has. Instead the template
+ * encodes attributes as a SET OF xxxxxxx. That is, it expects to encode
+ * each of the Attributes, not have them pre-encoded. Consequently an
+ * array of SECItems containing encoded Attributes is of no value to this
+ * function. But we cannot change the signature of this public function.
+ * It must continue to take SECItems.
+ *
+ * I have recoded this function so that each SECItem contains an
+ * encoded cert extension. The encoded cert extensions form the list for the
+ * single attribute of the cert request. In this implementation there is at most
+ * one attribute and it is always of type SEC_OID_PKCS9_EXTENSION_REQUEST.
+ */
+
+CERTCertificateRequest *
+CERT_CreateCertificateRequest(CERTName *subject,
+ CERTSubjectPublicKeyInfo *spki,
+ SECItem **attributes)
+{
+ CERTCertificateRequest *certreq;
+ PLArenaPool *arena;
+ CERTAttribute *attribute;
+ SECOidData *oidData;
+ SECStatus rv;
+ int i = 0;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ certreq = PORT_ArenaZNew(arena, CERTCertificateRequest);
+ if (!certreq) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ /* below here it is safe to goto loser */
+
+ certreq->arena = arena;
+
+ rv = DER_SetUInteger(arena, &certreq->version,
+ SEC_CERTIFICATE_REQUEST_VERSION);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = CERT_CopyName(arena, &certreq->subject, subject);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = SECKEY_CopySubjectPublicKeyInfo(arena,
+ &certreq->subjectPublicKeyInfo,
+ spki);
+ if (rv != SECSuccess)
+ goto loser;
+
+ certreq->attributes = PORT_ArenaZNewArray(arena, CERTAttribute *, 2);
+ if (!certreq->attributes)
+ goto loser;
+
+ /* Copy over attribute information */
+ if (!attributes || !attributes[0]) {
+ /*
+ ** Invent empty attribute information. According to the
+ ** pkcs#10 spec, attributes has this ASN.1 type:
+ **
+ ** attributes [0] IMPLICIT Attributes
+ **
+ ** Which means, we should create a NULL terminated list
+ ** with the first entry being NULL;
+ */
+ certreq->attributes[0] = NULL;
+ return certreq;
+ }
+
+ /* allocate space for attributes */
+ attribute = PORT_ArenaZNew(arena, CERTAttribute);
+ if (!attribute)
+ goto loser;
+
+ oidData = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST);
+ PORT_Assert(oidData);
+ if (!oidData)
+ goto loser;
+ rv = SECITEM_CopyItem(arena, &attribute->attrType, &oidData->oid);
+ if (rv != SECSuccess)
+ goto loser;
+
+ for (i = 0; attributes[i] != NULL; i++)
+ ;
+ attribute->attrValue = PORT_ArenaZNewArray(arena, SECItem *, i + 1);
+ if (!attribute->attrValue)
+ goto loser;
+
+ /* copy attributes */
+ for (i = 0; attributes[i]; i++) {
+ /*
+ ** Attributes are a SetOf Attribute which implies
+ ** lexigraphical ordering. It is assumes that the
+ ** attributes are passed in sorted. If we need to
+ ** add functionality to sort them, there is an
+ ** example in the PKCS 7 code.
+ */
+ attribute->attrValue[i] = SECITEM_ArenaDupItem(arena, attributes[i]);
+ if (!attribute->attrValue[i])
+ goto loser;
+ }
+
+ certreq->attributes[0] = attribute;
+
+ return certreq;
+
+loser:
+ CERT_DestroyCertificateRequest(certreq);
+ return NULL;
+}
+
+void
+CERT_DestroyCertificateRequest(CERTCertificateRequest *req)
+{
+ if (req && req->arena) {
+ PORT_FreeArena(req->arena, PR_FALSE);
+ }
+ return;
+}
+
+static void
+setCRExt(void *o, CERTCertExtension **exts)
+{
+ ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts;
+}
+
+/*
+** Set up to start gathering cert extensions for a cert request.
+** The list is created as CertExtensions and converted to an
+** attribute list by CERT_FinishCRAttributes().
+ */
+extern void *cert_StartExtensions(void *owner, PLArenaPool *ownerArena,
+ void (*setExts)(void *object, CERTCertExtension **exts));
+void *
+CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req)
+{
+ return (cert_StartExtensions((void *)req, req->arena, setCRExt));
+}
+
+/*
+** At entry req->attributes actually contains an list of cert extensions--
+** req-attributes is overloaded until the list is DER encoded (the first
+** ...EncodeItem() below).
+** We turn this into an attribute list by encapsulating it
+** in a PKCS 10 Attribute structure
+ */
+SECStatus
+CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req)
+{
+ SECItem *extlist;
+ SECOidData *oidrec;
+ CERTAttribute *attribute;
+
+ if (!req || !req->arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (req->attributes == NULL || req->attributes[0] == NULL)
+ return SECSuccess;
+
+ extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes,
+ SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate));
+ if (extlist == NULL)
+ return (SECFailure);
+
+ oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST);
+ if (oidrec == NULL)
+ return SECFailure;
+
+ /* now change the list of cert extensions into a list of attributes
+ */
+ req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute *, 2);
+
+ attribute = PORT_ArenaZNew(req->arena, CERTAttribute);
+
+ if (req->attributes == NULL || attribute == NULL ||
+ SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem *, 2);
+
+ if (attribute->attrValue == NULL)
+ return SECFailure;
+
+ attribute->attrValue[0] = extlist;
+ attribute->attrValue[1] = NULL;
+ req->attributes[0] = attribute;
+ req->attributes[1] = NULL;
+
+ return SECSuccess;
+}
+
+SECStatus
+CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req,
+ CERTCertExtension ***exts)
+{
+ if (req == NULL || exts == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (req->attributes == NULL || *req->attributes == NULL)
+ return SECSuccess;
+
+ if ((*req->attributes)->attrValue == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ return (SEC_ASN1DecodeItem(req->arena, exts,
+ SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate),
+ (*req->attributes)->attrValue[0]));
+}