summaryrefslogtreecommitdiffstats
path: root/security/nss/cmd/signtool/certgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/cmd/signtool/certgen.c')
-rw-r--r--security/nss/cmd/signtool/certgen.c713
1 files changed, 713 insertions, 0 deletions
diff --git a/security/nss/cmd/signtool/certgen.c b/security/nss/cmd/signtool/certgen.c
new file mode 100644
index 000000000..e095a01fb
--- /dev/null
+++ b/security/nss/cmd/signtool/certgen.c
@@ -0,0 +1,713 @@
+/* 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 "signtool.h"
+
+#include "secoid.h"
+#include "cryptohi.h"
+#include "certdb.h"
+
+static char *GetSubjectFromUser(unsigned long serial);
+static CERTCertificate *GenerateSelfSignedObjectSigningCert(char *nickname,
+ CERTCertDBHandle *db, char *subject, unsigned long serial, int keysize,
+ char *token);
+static SECStatus ChangeTrustAttributes(CERTCertDBHandle *db,
+ CERTCertificate *cert, char *trusts);
+static SECStatus set_cert_type(CERTCertificate *cert, unsigned int type);
+static SECItem *sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk);
+static CERTCertificate *install_cert(CERTCertDBHandle *db, SECItem *derCert,
+ char *nickname);
+static SECStatus GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
+ SECKEYPrivateKey **privk, int keysize);
+static CERTCertificateRequest *make_cert_request(char *subject,
+ SECKEYPublicKey *pubk);
+static CERTCertificate *make_cert(CERTCertificateRequest *req,
+ unsigned long serial, CERTName *ca_subject);
+static void output_ca_cert(CERTCertificate *cert, CERTCertDBHandle *db);
+
+/***********************************************************************
+ *
+ * G e n e r a t e C e r t
+ *
+ * Runs the whole process of creating a new cert, getting info from the
+ * user, etc.
+ */
+int
+GenerateCert(char *nickname, int keysize, char *token)
+{
+ CERTCertDBHandle *db;
+ CERTCertificate *cert;
+ char *subject;
+ unsigned long serial;
+ char stdinbuf[160];
+
+ /* Print warning about having the browser open */
+ PR_fprintf(PR_STDOUT /*always go to console*/,
+ "\nWARNING: Performing this operation while the browser is running could cause"
+ "\ncorruption of your security databases. If the browser is currently running,"
+ "\nyou should exit the browser before continuing this operation. Enter "
+ "\n\"y\" to continue, or anything else to abort: ");
+ pr_fgets(stdinbuf, 160, PR_STDIN);
+ PR_fprintf(PR_STDOUT, "\n");
+ if (tolower(stdinbuf[0]) != 'y') {
+ PR_fprintf(errorFD, "Operation aborted at user's request.\n");
+ errorCount++;
+ return -1;
+ }
+
+ db = CERT_GetDefaultCertDB();
+ if (!db) {
+ FatalError("Unable to open certificate database");
+ }
+
+ if (PK11_FindCertFromNickname(nickname, &pwdata)) {
+ PR_fprintf(errorFD,
+ "ERROR: Certificate with nickname \"%s\" already exists in database. You\n"
+ "must choose a different nickname.\n",
+ nickname);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ LL_L2UI(serial, PR_Now());
+
+ subject = GetSubjectFromUser(serial);
+ if (!subject) {
+ FatalError("Unable to get subject from user");
+ }
+
+ cert = GenerateSelfSignedObjectSigningCert(nickname, db, subject,
+ serial, keysize, token);
+
+ if (cert) {
+ output_ca_cert(cert, db);
+ CERT_DestroyCertificate(cert);
+ }
+
+ PORT_Free(subject);
+ return 0;
+}
+
+#undef VERBOSE_PROMPTS
+
+/*********************************************************************8
+ * G e t S u b j e c t F r o m U s e r
+ *
+ * Construct the subject information line for a certificate by querying
+ * the user on stdin.
+ */
+static char *
+GetSubjectFromUser(unsigned long serial)
+{
+ char buf[STDIN_BUF_SIZE];
+ char common_name_buf[STDIN_BUF_SIZE];
+ char *common_name, *state, *orgunit, *country, *org, *locality;
+ char *email, *uid;
+ char *subject;
+ char *cp;
+ int subjectlen = 0;
+
+ common_name = state = orgunit = country = org = locality = email =
+ uid = subject = NULL;
+
+ /* Get subject information */
+ PR_fprintf(PR_STDOUT,
+ "\nEnter certificate information. All fields are optional. Acceptable\n"
+ "characters are numbers, letters, spaces, and apostrophes.\n");
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nCOMMON NAME\n"
+ "Enter the full name you want to give your certificate. (Example: Test-Only\n"
+ "Object Signing Certificate)\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "certificate common name: ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(buf);
+ if (*cp == '\0') {
+ sprintf(common_name_buf, "%s (%lu)", DEFAULT_COMMON_NAME,
+ serial);
+ cp = common_name_buf;
+ }
+ common_name = PORT_ZAlloc(strlen(cp) + 6);
+ if (!common_name) {
+ out_of_memory();
+ }
+ sprintf(common_name, "CN=%s, ", cp);
+ subjectlen += strlen(common_name);
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nORGANIZATION NAME\n"
+ "Enter the name of your organization. For example, this could be the name\n"
+ "of your company.\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "organization: ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(buf);
+ if (*cp != '\0') {
+ org = PORT_ZAlloc(strlen(cp) + 5);
+ if (!org) {
+ out_of_memory();
+ }
+ sprintf(org, "O=%s, ", cp);
+ subjectlen += strlen(org);
+ }
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nORGANIZATION UNIT\n"
+ "Enter the name of your organization unit. For example, this could be the\n"
+ "name of your department.\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "organization unit: ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(buf);
+ if (*cp != '\0') {
+ orgunit = PORT_ZAlloc(strlen(cp) + 6);
+ if (!orgunit) {
+ out_of_memory();
+ }
+ sprintf(orgunit, "OU=%s, ", cp);
+ subjectlen += strlen(orgunit);
+ }
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nSTATE\n"
+ "Enter the name of your state or province.\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "state or province: ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(buf);
+ if (*cp != '\0') {
+ state = PORT_ZAlloc(strlen(cp) + 6);
+ if (!state) {
+ out_of_memory();
+ }
+ sprintf(state, "ST=%s, ", cp);
+ subjectlen += strlen(state);
+ }
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nCOUNTRY\n"
+ "Enter the 2-character abbreviation for the name of your country.\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "country (must be exactly 2 characters): ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(cp);
+ if (strlen(cp) != 2) {
+ *cp = '\0'; /* country code must be 2 chars */
+ }
+ if (*cp != '\0') {
+ country = PORT_ZAlloc(strlen(cp) + 5);
+ if (!country) {
+ out_of_memory();
+ }
+ sprintf(country, "C=%s, ", cp);
+ subjectlen += strlen(country);
+ }
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nUSERNAME\n"
+ "Enter your system username or UID\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "username: ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(buf);
+ if (*cp != '\0') {
+ uid = PORT_ZAlloc(strlen(cp) + 7);
+ if (!uid) {
+ out_of_memory();
+ }
+ sprintf(uid, "UID=%s, ", cp);
+ subjectlen += strlen(uid);
+ }
+
+#ifdef VERBOSE_PROMPTS
+ PR_fprintf(PR_STDOUT, "\nEMAIL ADDRESS\n"
+ "Enter your email address.\n"
+ "-->");
+#else
+ PR_fprintf(PR_STDOUT, "email address: ");
+#endif
+ if (!fgets(buf, STDIN_BUF_SIZE, stdin)) {
+ return NULL;
+ }
+ cp = chop(buf);
+ if (*cp != '\0') {
+ email = PORT_ZAlloc(strlen(cp) + 5);
+ if (!email) {
+ out_of_memory();
+ }
+ sprintf(email, "E=%s,", cp);
+ subjectlen += strlen(email);
+ }
+
+ subjectlen++;
+
+ subject = PORT_ZAlloc(subjectlen);
+ if (!subject) {
+ out_of_memory();
+ }
+
+ sprintf(subject, "%s%s%s%s%s%s%s",
+ common_name ? common_name : "",
+ org ? org : "",
+ orgunit ? orgunit : "",
+ state ? state : "",
+ country ? country : "",
+ uid ? uid : "",
+ email ? email : "");
+ if ((strlen(subject) > 1) && (subject[strlen(subject) - 1] == ' ')) {
+ subject[strlen(subject) - 2] = '\0';
+ }
+
+ PORT_Free(common_name);
+ PORT_Free(org);
+ PORT_Free(orgunit);
+ PORT_Free(state);
+ PORT_Free(country);
+ PORT_Free(uid);
+ PORT_Free(email);
+
+ return subject;
+}
+
+/**************************************************************************
+ *
+ * G e n e r a t e S e l f S i g n e d O b j e c t S i g n i n g C e r t
+ * *phew*^
+ *
+ */
+static CERTCertificate *
+GenerateSelfSignedObjectSigningCert(char *nickname, CERTCertDBHandle *db,
+ char *subject, unsigned long serial, int keysize, char *token)
+{
+ CERTCertificate *cert, *temp_cert;
+ SECItem *derCert;
+ CERTCertificateRequest *req;
+
+ PK11SlotInfo *slot = NULL;
+ SECKEYPrivateKey *privk = NULL;
+ SECKEYPublicKey *pubk = NULL;
+
+ if (token) {
+ slot = PK11_FindSlotByName(token);
+ } else {
+ slot = PK11_GetInternalKeySlot();
+ }
+
+ if (slot == NULL) {
+ PR_fprintf(errorFD, "Can't find PKCS11 slot %s\n",
+ token ? token : "");
+ errorCount++;
+ exit(ERRX);
+ }
+
+ if (GenerateKeyPair(slot, &pubk, &privk, keysize) != SECSuccess) {
+ FatalError("Error generating keypair.");
+ }
+ req = make_cert_request(subject, pubk);
+ temp_cert = make_cert(req, serial, &req->subject);
+ if (set_cert_type(temp_cert,
+ NS_CERT_TYPE_OBJECT_SIGNING |
+ NS_CERT_TYPE_OBJECT_SIGNING_CA) !=
+ SECSuccess) {
+ FatalError("Unable to set cert type");
+ }
+
+ derCert = sign_cert(temp_cert, privk);
+ cert = install_cert(db, derCert, nickname);
+ if (ChangeTrustAttributes(db, cert, ",,uC") != SECSuccess) {
+ FatalError("Unable to change trust on generated certificate");
+ }
+
+ /* !!! Free memory ? !!! */
+ PK11_FreeSlot(slot);
+ SECKEY_DestroyPrivateKey(privk);
+ SECKEY_DestroyPublicKey(pubk);
+ CERT_DestroyCertificate(temp_cert);
+ CERT_DestroyCertificateRequest(req);
+
+ return cert;
+}
+
+/**************************************************************************
+ *
+ * C h a n g e T r u s t A t t r i b u t e s
+ */
+static SECStatus
+ChangeTrustAttributes(CERTCertDBHandle *db, CERTCertificate *cert, char *trusts)
+{
+
+ CERTCertTrust *trust;
+
+ if (!db || !cert || !trusts) {
+ PR_fprintf(errorFD, "ChangeTrustAttributes got incomplete arguments.\n");
+ errorCount++;
+ return SECFailure;
+ }
+
+ trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
+ if (!trust) {
+ PR_fprintf(errorFD, "ChangeTrustAttributes unable to allocate "
+ "CERTCertTrust\n");
+ errorCount++;
+ return SECFailure;
+ }
+
+ if (CERT_DecodeTrustString(trust, trusts)) {
+ return SECFailure;
+ }
+
+ if (CERT_ChangeCertTrust(db, cert, trust)) {
+ PR_fprintf(errorFD, "unable to modify trust attributes for cert %s\n",
+ cert->nickname ? cert->nickname : "");
+ errorCount++;
+ return SECFailure;
+ }
+
+ PORT_Free(trust);
+ return SECSuccess;
+}
+
+/*************************************************************************
+ *
+ * s e t _ c e r t _ t y p e
+ */
+static SECStatus
+set_cert_type(CERTCertificate *cert, unsigned int type)
+{
+ void *context;
+ SECStatus status = SECSuccess;
+ SECItem certType;
+ char ctype;
+
+ context = CERT_StartCertExtensions(cert);
+
+ certType.type = siBuffer;
+ certType.data = (unsigned char *)&ctype;
+ certType.len = 1;
+ ctype = (unsigned char)type;
+ if (CERT_EncodeAndAddBitStrExtension(context, SEC_OID_NS_CERT_EXT_CERT_TYPE,
+ &certType, PR_TRUE /*critical*/) !=
+ SECSuccess) {
+ status = SECFailure;
+ }
+
+ if (CERT_FinishExtensions(context) != SECSuccess) {
+ status = SECFailure;
+ }
+
+ return status;
+}
+
+/********************************************************************
+ *
+ * s i g n _ c e r t
+ */
+static SECItem *
+sign_cert(CERTCertificate *cert, SECKEYPrivateKey *privk)
+{
+ SECStatus rv;
+
+ SECItem der2;
+ SECItem *result2;
+
+ SECOidTag alg = SEC_OID_UNKNOWN;
+
+ alg = SEC_GetSignatureAlgorithmOidTag(privk->keyType, SEC_OID_UNKNOWN);
+ if (alg == SEC_OID_UNKNOWN) {
+ FatalError("Unknown key type");
+ }
+
+ rv = SECOID_SetAlgorithmID(cert->arena, &cert->signature, alg, 0);
+
+ if (rv != SECSuccess) {
+ PR_fprintf(errorFD, "%s: unable to set signature alg id\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ der2.len = 0;
+ der2.data = NULL;
+
+ (void)SEC_ASN1EncodeItem(cert->arena, &der2, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
+
+ if (rv != SECSuccess) {
+ PR_fprintf(errorFD, "%s: error encoding cert\n", PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ result2 = (SECItem *)PORT_ArenaZAlloc(cert->arena, sizeof(SECItem));
+ if (result2 == NULL)
+ out_of_memory();
+
+ rv = SEC_DerSignData(cert->arena, result2, der2.data, der2.len, privk, alg);
+
+ if (rv != SECSuccess) {
+ PR_fprintf(errorFD, "can't sign encoded certificate data\n");
+ errorCount++;
+ exit(ERRX);
+ } else if (verbosity >= 0) {
+ PR_fprintf(outputFD, "certificate has been signed\n");
+ }
+
+ cert->derCert = *result2;
+
+ return result2;
+}
+
+/*********************************************************************
+ *
+ * i n s t a l l _ c e r t
+ *
+ * Installs the cert in the permanent database.
+ */
+static CERTCertificate *
+install_cert(CERTCertDBHandle *db, SECItem *derCert, char *nickname)
+{
+ CERTCertificate *newcert;
+ PK11SlotInfo *newSlot;
+
+ newSlot = PK11_ImportDERCertForKey(derCert, nickname, &pwdata);
+ if (newSlot == NULL) {
+ PR_fprintf(errorFD, "Unable to install certificate\n");
+ errorCount++;
+ exit(ERRX);
+ }
+
+ newcert = PK11_FindCertFromDERCertItem(newSlot, derCert, &pwdata);
+ PK11_FreeSlot(newSlot);
+ if (newcert == NULL) {
+ PR_fprintf(errorFD, "%s: can't find new certificate\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "certificate \"%s\" added to database\n",
+ nickname);
+ }
+
+ return newcert;
+}
+
+/******************************************************************
+ *
+ * G e n e r a t e K e y P a i r
+ */
+static SECStatus
+GenerateKeyPair(PK11SlotInfo *slot, SECKEYPublicKey **pubk,
+ SECKEYPrivateKey **privk, int keysize)
+{
+
+ PK11RSAGenParams rsaParams;
+
+ if (keysize == -1) {
+ rsaParams.keySizeInBits = DEFAULT_RSA_KEY_SIZE;
+ } else {
+ rsaParams.keySizeInBits = keysize;
+ }
+ rsaParams.pe = 0x10001;
+
+ if (PK11_Authenticate(slot, PR_FALSE /*loadCerts*/, &pwdata) !=
+ SECSuccess) {
+ SECU_PrintError(progName, "failure authenticating to key database.\n");
+ exit(ERRX);
+ }
+
+ *privk = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams,
+
+ pubk, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, &pwdata);
+
+ if (*privk != NULL && *pubk != NULL) {
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "generated public/private key pair\n");
+ }
+ } else {
+ SECU_PrintError(progName, "failure generating key pair\n");
+ exit(ERRX);
+ }
+
+ return SECSuccess;
+}
+
+/******************************************************************
+ *
+ * m a k e _ c e r t _ r e q u e s t
+ */
+static CERTCertificateRequest *
+make_cert_request(char *subject, SECKEYPublicKey *pubk)
+{
+ CERTName *subj;
+ CERTSubjectPublicKeyInfo *spki;
+
+ CERTCertificateRequest *req;
+
+ /* Create info about public key */
+ spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
+ if (!spki) {
+ SECU_PrintError(progName, "unable to create subject public key");
+ exit(ERRX);
+ }
+
+ subj = CERT_AsciiToName(subject);
+ if (subj == NULL) {
+ FatalError("Invalid data in certificate description");
+ }
+
+ /* Generate certificate request */
+ req = CERT_CreateCertificateRequest(subj, spki, 0);
+ if (!req) {
+ SECU_PrintError(progName, "unable to make certificate request");
+ exit(ERRX);
+ }
+
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ CERT_DestroyName(subj);
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "certificate request generated\n");
+ }
+
+ return req;
+}
+
+/******************************************************************
+ *
+ * m a k e _ c e r t
+ */
+static CERTCertificate *
+make_cert(CERTCertificateRequest *req, unsigned long serial,
+ CERTName *ca_subject)
+{
+ CERTCertificate *cert;
+
+ CERTValidity *validity = NULL;
+
+ PRTime now, after;
+ PRExplodedTime printableTime;
+
+ now = PR_Now();
+ PR_ExplodeTime(now, PR_GMTParameters, &printableTime);
+
+ printableTime.tm_month += 3;
+ after = PR_ImplodeTime(&printableTime);
+
+ validity = CERT_CreateValidity(now, after);
+
+ if (validity == NULL) {
+ PR_fprintf(errorFD, "%s: error creating certificate validity\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ cert = CERT_CreateCertificate(serial, ca_subject, validity, req);
+ CERT_DestroyValidity(validity);
+
+ if (cert == NULL) {
+ /* should probably be more precise here */
+ PR_fprintf(errorFD, "%s: error while generating certificate\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ return cert;
+}
+
+/*************************************************************************
+ *
+ * o u t p u t _ c a _ c e r t
+ */
+static void
+output_ca_cert(CERTCertificate *cert, CERTCertDBHandle *db)
+{
+ FILE *out;
+
+ SECItem *encodedCertChain;
+ SEC_PKCS7ContentInfo *certChain;
+ char *filename, *certData;
+
+ /* the raw */
+
+ filename = PORT_ZAlloc(strlen(DEFAULT_X509_BASENAME) + 8);
+ if (!filename)
+ out_of_memory();
+
+ sprintf(filename, "%s.raw", DEFAULT_X509_BASENAME);
+ if ((out = fopen(filename, "wb")) == NULL) {
+ PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
+ filename);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ certChain = SEC_PKCS7CreateCertsOnly(cert, PR_TRUE, db);
+ encodedCertChain =
+ SEC_PKCS7EncodeItem(NULL, NULL, certChain, NULL, NULL, NULL);
+ SEC_PKCS7DestroyContentInfo(certChain);
+
+ if (encodedCertChain) {
+ fprintf(out, "Content-type: application/x-x509-ca-cert\n\n");
+ fwrite(encodedCertChain->data, 1, encodedCertChain->len,
+ out);
+ SECITEM_FreeItem(encodedCertChain, PR_TRUE);
+ } else {
+ PR_fprintf(errorFD, "%s: Can't DER encode this certificate\n",
+ PROGRAM_NAME);
+ errorCount++;
+ exit(ERRX);
+ }
+
+ fclose(out);
+
+ /* and the cooked */
+
+ sprintf(filename, "%s.cacert", DEFAULT_X509_BASENAME);
+ if ((out = fopen(filename, "wb")) == NULL) {
+ PR_fprintf(errorFD, "%s: Can't open %s output file\n", PROGRAM_NAME,
+ filename);
+ errorCount++;
+ return;
+ }
+
+ certData = BTOA_DataToAscii(cert->derCert.data, cert->derCert.len);
+ fprintf(out, "%s\n%s\n%s\n", NS_CERT_HEADER, certData, NS_CERT_TRAILER);
+ PORT_Free(certData);
+
+ PORT_Free(filename);
+ fclose(out);
+
+ if (verbosity >= 0) {
+ PR_fprintf(outputFD, "Exported certificate to %s.raw and %s.cacert.\n",
+ DEFAULT_X509_BASENAME, DEFAULT_X509_BASENAME);
+ }
+}