diff options
Diffstat (limited to 'security/nss/cmd/signtool/certgen.c')
-rw-r--r-- | security/nss/cmd/signtool/certgen.c | 738 |
1 files changed, 738 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..a08d98b6f --- /dev/null +++ b/security/nss/cmd/signtool/certgen.c @@ -0,0 +1,738 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#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); + + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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 + fgets(buf, STDIN_BUF_SIZE, stdin); + 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); + + 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; + } + + 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; + + void *dummy; + 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; + + dummy = 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); + } + + 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); + + 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; + + /* 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; + } + + fprintf (out, "%s\n%s\n%s\n", + NS_CERT_HEADER, + BTOA_DataToAscii (cert->derCert.data, cert->derCert.len), + NS_CERT_TRAILER); + + fclose (out); + + if (verbosity >= 0) { + PR_fprintf(outputFD, "Exported certificate to %s.raw and %s.cacert.\n", + DEFAULT_X509_BASENAME, DEFAULT_X509_BASENAME); + } +} + + |