/* 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/. */ /* ** certutil.c ** ** utility for managing certificates and the cert database ** */ #include #include #include #if defined(WIN32) #include "fcntl.h" #include "io.h" #endif #include "secutil.h" #if defined(XP_UNIX) #include #endif #include "nspr.h" #include "prtypes.h" #include "prtime.h" #include "prlong.h" #include "pk11func.h" #include "secasn1.h" #include "cert.h" #include "cryptohi.h" #include "secoid.h" #include "certdb.h" #include "nss.h" #include "certutil.h" #include "basicutil.h" #include "ssl.h" #define MIN_KEY_BITS 512 /* MAX_KEY_BITS should agree with RSA_MAX_MODULUS_BITS in freebl */ #define MAX_KEY_BITS 8192 #define DEFAULT_KEY_BITS 2048 #define GEN_BREAK(e) \ rv = e; \ break; char *progName; static SECStatus ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert, CERTCertTrust *trust, PK11SlotInfo *slot, void *pwdata) { SECStatus rv; rv = CERT_ChangeCertTrust(handle, cert, trust); if (rv != SECSuccess) { if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { PK11SlotInfo *internalslot; internalslot = PK11_GetInternalKeySlot(); rv = PK11_Authenticate(internalslot, PR_TRUE, pwdata); if (rv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(internalslot)); PK11_FreeSlot(internalslot); return SECFailure; } PK11_FreeSlot(internalslot); } rv = CERT_ChangeCertTrust(handle, cert, trust); } } return rv; } static CERTCertificateRequest * GetCertRequest(const SECItem *reqDER, void *pwarg) { CERTCertificateRequest *certReq = NULL; CERTSignedData signedData; PLArenaPool *arena = NULL; SECStatus rv; do { arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { GEN_BREAK(SECFailure); } certReq = (CERTCertificateRequest *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificateRequest)); if (!certReq) { GEN_BREAK(SECFailure); } certReq->arena = arena; /* Since cert request is a signed data, must decode to get the inner data */ PORT_Memset(&signedData, 0, sizeof(signedData)); rv = SEC_ASN1DecodeItem(arena, &signedData, SEC_ASN1_GET(CERT_SignedDataTemplate), reqDER); if (rv) { break; } rv = SEC_ASN1DecodeItem(arena, certReq, SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data); if (rv) { break; } rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData, &certReq->subjectPublicKeyInfo, pwarg); } while (0); if (rv) { SECU_PrintError(progName, "bad certificate request\n"); if (arena) { PORT_FreeArena(arena, PR_FALSE); } certReq = NULL; } return certReq; } static SECStatus AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, char *name, char *trusts, const SECItem *certDER, PRBool emailcert, void *pwdata) { CERTCertTrust *trust = NULL; CERTCertificate *cert = NULL; SECStatus rv; do { /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)certDER->data, certDER->len); if (!cert) { SECU_PrintError(progName, "could not decode certificate"); GEN_BREAK(SECFailure); } /* Create a cert trust */ trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); if (!trust) { SECU_PrintError(progName, "unable to allocate cert trust"); GEN_BREAK(SECFailure); } rv = CERT_DecodeTrustString(trust, trusts); if (rv) { SECU_PrintError(progName, "unable to decode trust string"); GEN_BREAK(SECFailure); } rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); if (rv != SECSuccess) { /* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have * been coded to take a password arg. */ if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); GEN_BREAK(SECFailure); } rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); } if (rv != SECSuccess) { SECU_PrintError(progName, "could not add certificate to token or database"); GEN_BREAK(SECFailure); } } rv = ChangeCertTrust(handle, cert, trust, slot, pwdata); if (rv != SECSuccess) { SECU_PrintError(progName, "could not change trust on certificate"); GEN_BREAK(SECFailure); } if (emailcert) { CERT_SaveSMimeProfile(cert, NULL, pwdata); } } while (0); CERT_DestroyCertificate(cert); PORT_Free(trust); return rv; } static SECStatus CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, SECOidTag hashAlgTag, CERTName *subject, const char *phone, int ascii, const char *emailAddrs, const char *dnsNames, certutilExtnList extnList, const char *extGeneric, PRBool pssCertificate, /*out*/ SECItem *result) { CERTSubjectPublicKeyInfo *spki; CERTCertificateRequest *cr; SECItem *encoding; SECOidTag signAlgTag; SECStatus rv; PLArenaPool *arena; void *extHandle; SECItem signedReq = { siBuffer, NULL, 0 }; SECAlgorithmID signAlg; SECItem *params = NULL; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { SECU_PrintError(progName, "out of memory"); return SECFailure; } /* Create info about public key */ spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); if (!spki) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "unable to create subject public key"); return SECFailure; } /* Change cert type to RSA-PSS, if desired. */ if (pssCertificate) { params = SEC_CreateSignatureAlgorithmParameters(arena, NULL, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlgTag, NULL, privk); if (!params) { PORT_FreeArena(arena, PR_FALSE); SECKEY_DestroySubjectPublicKeyInfo(spki); SECU_PrintError(progName, "unable to create RSA-PSS parameters"); return SECFailure; } spki->algorithm.parameters.data = NULL; rv = SECOID_SetAlgorithmID(arena, &spki->algorithm, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlgTag == SEC_OID_UNKNOWN ? NULL : params); if (rv != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); SECKEY_DestroySubjectPublicKeyInfo(spki); SECU_PrintError(progName, "unable to set algorithm ID"); return SECFailure; } } /* Generate certificate request */ cr = CERT_CreateCertificateRequest(subject, spki, NULL); SECKEY_DestroySubjectPublicKeyInfo(spki); if (!cr) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "unable to make certificate request"); return SECFailure; } extHandle = CERT_StartCertificateRequestAttributes(cr); if (extHandle == NULL) { PORT_FreeArena(arena, PR_FALSE); CERT_DestroyCertificateRequest(cr); return SECFailure; } if (AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric) != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); CERT_FinishExtensions(extHandle); CERT_DestroyCertificateRequest(cr); return SECFailure; } CERT_FinishExtensions(extHandle); CERT_FinishCertificateRequestAttributes(cr); /* Der encode the request */ encoding = SEC_ASN1EncodeItem(arena, NULL, cr, SEC_ASN1_GET(CERT_CertificateRequestTemplate)); CERT_DestroyCertificateRequest(cr); if (encoding == NULL) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "der encoding of request failed"); return SECFailure; } PORT_Memset(&signAlg, 0, sizeof(signAlg)); if (pssCertificate) { rv = SECOID_SetAlgorithmID(arena, &signAlg, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params); if (rv != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "unable to set algorithm ID"); return SECFailure; } } else { signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); if (signAlgTag == SEC_OID_UNKNOWN) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "unknown Key or Hash type"); return SECFailure; } rv = SECOID_SetAlgorithmID(arena, &signAlg, signAlgTag, 0); if (rv != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "unable to set algorithm ID"); return SECFailure; } } /* Sign the request */ rv = SEC_DerSignDataWithAlgorithmID(arena, &signedReq, encoding->data, encoding->len, privk, &signAlg); if (rv) { PORT_FreeArena(arena, PR_FALSE); SECU_PrintError(progName, "signing of data failed"); return SECFailure; } /* Encode request in specified format */ if (ascii) { char *obuf; char *header, *name, *email, *org, *state, *country; obuf = BTOA_ConvertItemToAscii(&signedReq); if (!obuf) { goto oom; } name = CERT_GetCommonName(subject); if (!name) { name = PORT_Strdup("(not specified)"); } if (!phone) phone = "(not specified)"; email = CERT_GetCertEmailAddress(subject); if (!email) email = PORT_Strdup("(not specified)"); org = CERT_GetOrgName(subject); if (!org) org = PORT_Strdup("(not specified)"); state = CERT_GetStateName(subject); if (!state) state = PORT_Strdup("(not specified)"); country = CERT_GetCountryName(subject); if (!country) country = PORT_Strdup("(not specified)"); header = PR_smprintf( "\nCertificate request generated by Netscape certutil\n" "Phone: %s\n\n" "Common Name: %s\n" "Email: %s\n" "Organization: %s\n" "State: %s\n" "Country: %s\n\n" "%s\n", phone, name, email, org, state, country, NS_CERTREQ_HEADER); PORT_Free(name); PORT_Free(email); PORT_Free(org); PORT_Free(state); PORT_Free(country); if (header) { char *trailer = PR_smprintf("\n%s\n", NS_CERTREQ_TRAILER); if (trailer) { PRUint32 headerLen = PL_strlen(header); PRUint32 obufLen = PL_strlen(obuf); PRUint32 trailerLen = PL_strlen(trailer); SECITEM_AllocItem(NULL, result, headerLen + obufLen + trailerLen); if (result->data) { PORT_Memcpy(result->data, header, headerLen); PORT_Memcpy(result->data + headerLen, obuf, obufLen); PORT_Memcpy(result->data + headerLen + obufLen, trailer, trailerLen); } PR_smprintf_free(trailer); } PR_smprintf_free(header); } PORT_Free(obuf); } else { (void)SECITEM_CopyItem(NULL, result, &signedReq); } if (!result->data) { oom: SECU_PrintError(progName, "out of memory"); PORT_SetError(SEC_ERROR_NO_MEMORY); rv = SECFailure; } PORT_FreeArena(arena, PR_FALSE); return rv; } static SECStatus ChangeTrustAttributes(CERTCertDBHandle *handle, PK11SlotInfo *slot, char *name, char *trusts, void *pwdata) { SECStatus rv; CERTCertificate *cert; CERTCertTrust *trust; cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata); if (!cert) { SECU_PrintError(progName, "could not find certificate named \"%s\"", name); return SECFailure; } trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); if (!trust) { SECU_PrintError(progName, "unable to allocate cert trust"); return SECFailure; } /* This function only decodes these characters: pPwcTCu, */ rv = CERT_DecodeTrustString(trust, trusts); if (rv) { SECU_PrintError(progName, "unable to decode trust string"); return SECFailure; } /* CERT_ChangeCertTrust API does not have a way to pass in * a context, so NSS can't prompt for the password if it needs to. * check to see if the failure was token not logged in and * log in if need be. */ rv = ChangeCertTrust(handle, cert, trust, slot, pwdata); if (rv != SECSuccess) { SECU_PrintError(progName, "unable to modify trust attributes"); return SECFailure; } CERT_DestroyCertificate(cert); PORT_Free(trust); return SECSuccess; } static SECStatus DumpChain(CERTCertDBHandle *handle, char *name, PRBool ascii, PRBool simpleSelfSigned) { CERTCertificate *the_cert; CERTCertificateList *chain; int i, j; the_cert = SECU_FindCertByNicknameOrFilename(handle, name, ascii, NULL); if (!the_cert) { SECU_PrintError(progName, "Could not find: %s\n", name); return SECFailure; } if (simpleSelfSigned && SECEqual == SECITEM_CompareItem(&the_cert->derIssuer, &the_cert->derSubject)) { printf("\"%s\" [%s]\n\n", the_cert->nickname, the_cert->subjectName); CERT_DestroyCertificate(the_cert); return SECSuccess; } chain = CERT_CertChainFromCert(the_cert, 0, PR_TRUE); CERT_DestroyCertificate(the_cert); if (!chain) { SECU_PrintError(progName, "Could not obtain chain for: %s\n", name); return SECFailure; } for (i = chain->len - 1; i >= 0; i--) { CERTCertificate *c; c = CERT_FindCertByDERCert(handle, &chain->certs[i]); for (j = i; j < chain->len - 1; j++) { printf(" "); } if (c) { printf("\"%s\" [%s]\n\n", c->nickname, c->subjectName); CERT_DestroyCertificate(c); } else { printf("(null)\n\n"); } } CERT_DestroyCertificateList(chain); return SECSuccess; } static SECStatus outputCertOrExtension(CERTCertificate *the_cert, PRBool raw, PRBool ascii, SECItem *extensionOID, PRFileDesc *outfile) { SECItem data; PRInt32 numBytes; SECStatus rv = SECFailure; if (extensionOID) { int i; PRBool found = PR_FALSE; for (i = 0; the_cert->extensions[i] != NULL; i++) { CERTCertExtension *extension = the_cert->extensions[i]; if (SECITEM_CompareItem(&extension->id, extensionOID) == SECEqual) { found = PR_TRUE; numBytes = PR_Write(outfile, extension->value.data, extension->value.len); rv = SECSuccess; if (numBytes != (PRInt32)extension->value.len) { SECU_PrintSystemError(progName, "error writing extension"); rv = SECFailure; } break; } } if (!found) { SECU_PrintSystemError(progName, "extension not found"); rv = SECFailure; } } else { data.data = the_cert->derCert.data; data.len = the_cert->derCert.len; if (ascii) { PR_fprintf(outfile, "%s\n%s\n%s\n", NS_CERT_HEADER, BTOA_DataToAscii(data.data, data.len), NS_CERT_TRAILER); rv = SECSuccess; } else if (raw) { numBytes = PR_Write(outfile, data.data, data.len); rv = SECSuccess; if (numBytes != (PRInt32)data.len) { SECU_PrintSystemError(progName, "error writing raw cert"); rv = SECFailure; } } else { rv = SEC_PrintCertificateAndTrust(the_cert, "Certificate", NULL); if (rv != SECSuccess) { SECU_PrintError(progName, "problem printing certificate"); } } } return rv; } static SECStatus listCerts(CERTCertDBHandle *handle, char *name, char *email, PK11SlotInfo *slot, PRBool raw, PRBool ascii, SECItem *extensionOID, PRFileDesc *outfile, void *pwarg) { SECStatus rv = SECFailure; CERTCertList *certs; CERTCertListNode *node; /* List certs on a non-internal slot. */ if (!PK11_IsFriendly(slot) && PK11_NeedLogin(slot)) { SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, pwarg); if (newrv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); return SECFailure; } } if (name) { CERTCertificate *the_cert = SECU_FindCertByNicknameOrFilename(handle, name, ascii, NULL); if (!the_cert) { SECU_PrintError(progName, "Could not find cert: %s\n", name); return SECFailure; } /* Here, we have one cert with the desired nickname or email * address. Now, we will attempt to get a list of ALL certs * with the same subject name as the cert we have. That list * should contain, at a minimum, the one cert we have already found. * If the list of certs is empty (NULL), the libraries have failed. */ certs = CERT_CreateSubjectCertList(NULL, handle, &the_cert->derSubject, PR_Now(), PR_FALSE); CERT_DestroyCertificate(the_cert); if (!certs) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); SECU_PrintError(progName, "problem printing certificates"); return SECFailure; } for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node, certs); node = CERT_LIST_NEXT(node)) { rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID, outfile); if (rv != SECSuccess) { break; } } } else if (email) { certs = PK11_FindCertsFromEmailAddress(email, NULL); if (!certs) { SECU_PrintError(progName, "Could not find certificates for email address: %s\n", email); return SECFailure; } for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node, certs); node = CERT_LIST_NEXT(node)) { rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID, outfile); if (rv != SECSuccess) { break; } } } else { certs = PK11_ListCertsInSlot(slot); if (certs) { for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node, certs); node = CERT_LIST_NEXT(node)) { SECU_PrintCertNickname(node, stdout); } rv = SECSuccess; } } if (certs) { CERT_DestroyCertList(certs); } if (rv) { SECU_PrintError(progName, "problem printing certificate nicknames"); return SECFailure; } return SECSuccess; /* not rv ?? */ } static SECStatus ListCerts(CERTCertDBHandle *handle, char *nickname, char *email, PK11SlotInfo *slot, PRBool raw, PRBool ascii, SECItem *extensionOID, PRFileDesc *outfile, secuPWData *pwdata) { SECStatus rv; if (slot && PK11_NeedUserInit(slot)) { printf("\nDatabase needs user init\n"); } if (!ascii && !raw && !nickname && !email) { PR_fprintf(outfile, "\n%-60s %-5s\n%-60s %-5s\n\n", "Certificate Nickname", "Trust Attributes", "", "SSL,S/MIME,JAR/XPI"); } if (slot == NULL) { CERTCertList *list; CERTCertListNode *node; list = PK11_ListCerts(PK11CertListAll, pwdata); for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); node = CERT_LIST_NEXT(node)) { SECU_PrintCertNickname(node, stdout); } CERT_DestroyCertList(list); return SECSuccess; } rv = listCerts(handle, nickname, email, slot, raw, ascii, extensionOID, outfile, pwdata); return rv; } static SECStatus DeleteCert(CERTCertDBHandle *handle, char *name, void *pwdata) { SECStatus rv; CERTCertificate *cert; cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata); if (!cert) { SECU_PrintError(progName, "could not find certificate named \"%s\"", name); return SECFailure; } rv = SEC_DeletePermCertificate(cert); CERT_DestroyCertificate(cert); if (rv) { SECU_PrintError(progName, "unable to delete certificate"); } return rv; } static SECStatus RenameCert(CERTCertDBHandle *handle, char *name, char *newName, void *pwdata) { SECStatus rv; CERTCertificate *cert; cert = CERT_FindCertByNicknameOrEmailAddrCX(handle, name, pwdata); if (!cert) { SECU_PrintError(progName, "could not find certificate named \"%s\"", name); return SECFailure; } rv = __PK11_SetCertificateNickname(cert, newName); CERT_DestroyCertificate(cert); if (rv) { SECU_PrintError(progName, "unable to rename certificate"); } return rv; } static SECStatus ValidateCert(CERTCertDBHandle *handle, char *name, char *date, char *certUsage, PRBool checkSig, PRBool logit, PRBool ascii, secuPWData *pwdata) { SECStatus rv; CERTCertificate *cert = NULL; PRTime timeBoundary; SECCertificateUsage usage; CERTVerifyLog reallog; CERTVerifyLog *log = NULL; if (!certUsage) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return (SECFailure); } switch (*certUsage) { case 'O': usage = certificateUsageStatusResponder; break; case 'L': usage = certificateUsageSSLCA; break; case 'A': usage = certificateUsageAnyCA; break; case 'Y': usage = certificateUsageVerifyCA; break; case 'C': usage = certificateUsageSSLClient; break; case 'V': usage = certificateUsageSSLServer; break; case 'I': usage = certificateUsageIPsec; break; case 'S': usage = certificateUsageEmailSigner; break; case 'R': usage = certificateUsageEmailRecipient; break; case 'J': usage = certificateUsageObjectSigner; break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); return (SECFailure); } do { cert = SECU_FindCertByNicknameOrFilename(handle, name, ascii, NULL); if (!cert) { SECU_PrintError(progName, "could not find certificate named \"%s\"", name); GEN_BREAK(SECFailure) } if (date != NULL) { rv = DER_AsciiToTime(&timeBoundary, date); if (rv) { SECU_PrintError(progName, "invalid input date"); GEN_BREAK(SECFailure) } } else { timeBoundary = PR_Now(); } if (logit) { log = &reallog; log->count = 0; log->head = NULL; log->tail = NULL; log->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (log->arena == NULL) { SECU_PrintError(progName, "out of memory"); GEN_BREAK(SECFailure) } } rv = CERT_VerifyCertificate(handle, cert, checkSig, usage, timeBoundary, pwdata, log, &usage); if (log) { if (log->head == NULL) { fprintf(stdout, "%s: certificate is valid\n", progName); GEN_BREAK(SECSuccess) } else { char *nick; CERTVerifyLogNode *node; node = log->head; while (node) { if (node->cert->nickname != NULL) { nick = node->cert->nickname; } else { nick = node->cert->subjectName; } fprintf(stderr, "%s : %s\n", nick, SECU_Strerror(node->error)); CERT_DestroyCertificate(node->cert); node = node->next; } } } else { if (rv != SECSuccess) { PRErrorCode perr = PORT_GetError(); fprintf(stdout, "%s: certificate is invalid: %s\n", progName, SECU_Strerror(perr)); GEN_BREAK(SECFailure) } fprintf(stdout, "%s: certificate is valid\n", progName); GEN_BREAK(SECSuccess) } } while (0); if (cert) { CERT_DestroyCertificate(cert); } return (rv); } static PRBool ItemIsPrintableASCII(const SECItem *item) { unsigned char *src = item->data; unsigned int len = item->len; while (len-- > 0) { unsigned char uc = *src++; if (uc < 0x20 || uc > 0x7e) return PR_FALSE; } return PR_TRUE; } /* Caller ensures that dst is at least item->len*2+1 bytes long */ static void SECItemToHex(const SECItem *item, char *dst) { if (dst && item && item->data) { unsigned char *src = item->data; unsigned int len = item->len; for (; len > 0; --len, dst += 2) { snprintf(dst, 3, "%02x", *src++); } *dst = '\0'; } } static const char *const keyTypeName[] = { "null", "rsa", "dsa", "fortezza", "dh", "kea", "ec", "rsaPss", "rsaOaep" }; #define MAX_CKA_ID_BIN_LEN 20 #define MAX_CKA_ID_STR_LEN 40 /* output human readable key ID in buffer, which should have at least * MAX_CKA_ID_STR_LEN + 3 octets (quotations and a null terminator) */ static void formatPrivateKeyID(SECKEYPrivateKey *privkey, char *buffer) { SECItem *ckaID; ckaID = PK11_GetLowLevelKeyIDForPrivateKey(privkey); if (!ckaID) { strcpy(buffer, "(no CKA_ID)"); } else if (ItemIsPrintableASCII(ckaID)) { int len = PR_MIN(MAX_CKA_ID_STR_LEN, ckaID->len); buffer[0] = '"'; memcpy(buffer + 1, ckaID->data, len); buffer[1 + len] = '"'; buffer[2 + len] = '\0'; } else { /* print ckaid in hex */ SECItem idItem = *ckaID; if (idItem.len > MAX_CKA_ID_BIN_LEN) idItem.len = MAX_CKA_ID_BIN_LEN; SECItemToHex(&idItem, buffer); } SECITEM_ZfreeItem(ckaID, PR_TRUE); } /* print key number, key ID (in hex or ASCII), key label (nickname) */ static SECStatus PrintKey(PRFileDesc *out, const char *nickName, int count, SECKEYPrivateKey *key, void *pwarg) { char ckaIDbuf[MAX_CKA_ID_STR_LEN + 4]; CERTCertificate *cert; KeyType keyType; formatPrivateKeyID(key, ckaIDbuf); cert = PK11_GetCertFromPrivateKey(key); if (cert) { keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo); CERT_DestroyCertificate(cert); } else { keyType = key->keyType; } PR_fprintf(out, "<%2d> %-8.8s %-42.42s %s\n", count, keyTypeName[keyType], ckaIDbuf, nickName); return SECSuccess; } /* returns SECSuccess if ANY keys are found, SECFailure otherwise. */ static SECStatus ListKeysInSlot(PK11SlotInfo *slot, const char *nickName, KeyType keyType, void *pwarg) { SECKEYPrivateKeyList *list; SECKEYPrivateKeyListNode *node; int count = 0; if (PK11_NeedLogin(slot)) { SECStatus rv = PK11_Authenticate(slot, PR_TRUE, pwarg); if (rv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); return SECFailure; } } if (nickName && nickName[0]) list = PK11_ListPrivKeysInSlot(slot, (char *)nickName, pwarg); else list = PK11_ListPrivateKeysInSlot(slot); if (list == NULL) { SECU_PrintError(progName, "problem listing keys"); return SECFailure; } for (node = PRIVKEY_LIST_HEAD(list); !PRIVKEY_LIST_END(node, list); node = PRIVKEY_LIST_NEXT(node)) { char *keyName; static const char orphan[] = { "(orphan)" }; if (keyType != nullKey && keyType != node->key->keyType) continue; keyName = PK11_GetPrivateKeyNickname(node->key); if (!keyName || !keyName[0]) { /* Try extra hard to find nicknames for keys that lack them. */ CERTCertificate *cert; PORT_Free((void *)keyName); keyName = NULL; cert = PK11_GetCertFromPrivateKey(node->key); if (cert) { if (cert->nickname && cert->nickname[0]) { keyName = PORT_Strdup(cert->nickname); } else if (cert->emailAddr && cert->emailAddr[0]) { keyName = PORT_Strdup(cert->emailAddr); } CERT_DestroyCertificate(cert); } } if (nickName) { if (!keyName || PL_strcmp(keyName, nickName)) { /* PKCS#11 module returned unwanted keys */ PORT_Free((void *)keyName); continue; } } if (!keyName) keyName = (char *)orphan; PrintKey(PR_STDOUT, keyName, count, node->key, pwarg); if (keyName != (char *)orphan) PORT_Free((void *)keyName); count++; } SECKEY_DestroyPrivateKeyList(list); if (count == 0) { PR_fprintf(PR_STDOUT, "%s: no keys found\n", progName); return SECFailure; } return SECSuccess; } /* returns SECSuccess if ANY keys are found, SECFailure otherwise. */ static SECStatus ListKeys(PK11SlotInfo *slot, const char *nickName, int index, KeyType keyType, PRBool dopriv, secuPWData *pwdata) { SECStatus rv = SECFailure; static const char fmt[] = "%s: Checking token \"%.33s\" in slot \"%.65s\"\n"; if (slot == NULL) { PK11SlotList *list; PK11SlotListElement *le; list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, pwdata); if (list) { for (le = list->head; le; le = le->next) { PR_fprintf(PR_STDOUT, fmt, progName, PK11_GetTokenName(le->slot), PK11_GetSlotName(le->slot)); rv &= ListKeysInSlot(le->slot, nickName, keyType, pwdata); } PK11_FreeSlotList(list); } } else { PR_fprintf(PR_STDOUT, fmt, progName, PK11_GetTokenName(slot), PK11_GetSlotName(slot)); rv = ListKeysInSlot(slot, nickName, keyType, pwdata); } return rv; } static SECStatus DeleteCertAndKey(char *nickname, secuPWData *pwdata) { SECStatus rv; CERTCertificate *cert; PK11SlotInfo *slot; slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); return SECFailure; } } cert = PK11_FindCertFromNickname(nickname, pwdata); if (!cert) { PK11_FreeSlot(slot); return SECFailure; } rv = PK11_DeleteTokenCertAndKey(cert, pwdata); if (rv != SECSuccess) { SECU_PrintError("problem deleting private key \"%s\"\n", nickname); } CERT_DestroyCertificate(cert); PK11_FreeSlot(slot); return rv; } static SECKEYPrivateKey * findPrivateKeyByID(PK11SlotInfo *slot, const char *ckaID, secuPWData *pwarg) { PORTCheapArenaPool arena; SECItem ckaIDItem = { 0 }; SECKEYPrivateKey *privkey = NULL; SECStatus rv; if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwarg); if (rv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); return NULL; } } if (0 == PL_strncasecmp("0x", ckaID, 2)) { ckaID += 2; /* skip leading "0x" */ } PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); if (SECU_HexString2SECItem(&arena.arena, &ckaIDItem, ckaID)) { privkey = PK11_FindKeyByKeyID(slot, &ckaIDItem, pwarg); } PORT_DestroyCheapArena(&arena); return privkey; } static SECStatus DeleteKey(SECKEYPrivateKey *privkey, secuPWData *pwarg) { SECStatus rv; PK11SlotInfo *slot; slot = PK11_GetSlotFromPrivateKey(privkey); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwarg); if (rv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); return SECFailure; } } rv = PK11_DeleteTokenPrivateKey(privkey, PR_TRUE); if (rv != SECSuccess) { char ckaIDbuf[MAX_CKA_ID_STR_LEN + 4]; formatPrivateKeyID(privkey, ckaIDbuf); SECU_PrintError("problem deleting private key \"%s\"\n", ckaIDbuf); } PK11_FreeSlot(slot); return rv; } /* * L i s t M o d u l e s * * Print a list of the PKCS11 modules that are * available. This is useful for smartcard people to * make sure they have the drivers loaded. * */ static SECStatus ListModules(void) { PK11SlotList *list; PK11SlotListElement *le; /* get them all! */ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, NULL); if (list == NULL) return SECFailure; /* look at each slot*/ for (le = list->head; le; le = le->next) { char *token_uri = PK11_GetTokenURI(le->slot); printf("\n"); printf(" slot: %s\n", PK11_GetSlotName(le->slot)); printf(" token: %s\n", PK11_GetTokenName(le->slot)); printf(" uri: %s\n", token_uri); PORT_Free(token_uri); } PK11_FreeSlotList(list); return SECSuccess; } static void PrintBuildFlags() { #ifdef NSS_FIPS_DISABLED PR_fprintf(PR_STDOUT, "NSS_FIPS_DISABLED\n"); #endif #ifdef NSS_NO_INIT_SUPPORT PR_fprintf(PR_STDOUT, "NSS_NO_INIT_SUPPORT\n"); #endif exit(0); } static void PrintSyntax() { #define FPS fprintf(stderr, FPS "Type %s -H for more detailed descriptions\n", progName); FPS "Usage: %s -N [-d certdir] [-P dbprefix] [-f pwfile] [--empty-password]\n", progName); FPS "Usage: %s -T [-d certdir] [-P dbprefix] [-h token-name]\n" "\t\t [-f pwfile] [-0 SSO-password]\n", progName); FPS "\t%s -A -n cert-name -t trustargs [-d certdir] [-P dbprefix] [-a] [-i input]\n", progName); FPS "\t%s -B -i batch-file\n", progName); FPS "\t%s -C [-c issuer-name | -x] -i cert-request-file -o cert-file\n" "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n" "\t\t [-f pwfile] [-d certdir] [-P dbprefix] [-Z hashAlg]\n" "\t\t [-1 | --keyUsage [keyUsageKeyword,..]] [-2] [-3] [-4]\n" "\t\t [-5 | --nsCertType [nsCertTypeKeyword,...]]\n" "\t\t [-6 | --extKeyUsage [extKeyUsageKeyword,...]] [-7 emailAddrs]\n" "\t\t [-8 dns-names] [-a]\n", progName); FPS "\t%s -D -n cert-name [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s --rename -n cert-name --new-n new-cert-name\n" "\t\t [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -E -n cert-name -t trustargs [-d certdir] [-P dbprefix] [-a] [-i input]\n", progName); FPS "\t%s -F -n cert-name [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -F -k key-id [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -G -n key-name [-h token-name] [-k rsa] [-g key-size] [-y exp]\n" "\t\t [-f pwfile] [-z noisefile] [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -G [-h token-name] -k dsa [-q pqgfile -g key-size] [-f pwfile]\n" "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -G [-h token-name] -k ec -q curve [-f pwfile]\n" "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|ec|rsa|all]\n", progName); FPS "\t\t [-f pwfile] [-X] [-d certdir] [-P dbprefix]\n"); FPS "\t%s --upgrade-merge --source-dir upgradeDir --upgrade-id uniqueID\n", progName); FPS "\t\t [--upgrade-token-name tokenName] [-d targetDBDir]\n"); FPS "\t\t [-P targetDBPrefix] [--source-prefix upgradeDBPrefix]\n"); FPS "\t\t [-f targetPWfile] [-@ upgradePWFile]\n"); FPS "\t%s --merge --source-dir sourceDBDir [-d targetDBdir]\n", progName); FPS "\t\t [-P targetDBPrefix] [--source-prefix sourceDBPrefix]\n"); FPS "\t\t [-f targetPWfile] [-@ sourcePWFile]\n"); FPS "\t%s -L [-n cert-name] [-h token-name] [--email email-address]\n", progName); FPS "\t\t [-X] [-r] [-a] [--dump-ext-val OID] [-d certdir] [-P dbprefix]\n"); FPS "\t%s --build-flags\n", progName); FPS "\t%s -M -n cert-name -t trustargs [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n" "\t\t [--simple-self-signed]\n", progName); FPS "\t%s -R -s subj -o cert-request-file [-d certdir] [-P dbprefix] [-p phone] [-a]\n" "\t\t [-7 emailAddrs] [-k key-type-or-id] [-h token-name] [-f pwfile]\n" "\t\t [-g key-size] [-Z hashAlg]\n", progName); FPS "\t%s -V -n cert-name -u usage [-b time] [-e] [-a]\n" "\t\t[-X] [-d certdir] [-P dbprefix]\n", progName); FPS "Usage: %s -W [-d certdir] [-f pwfile] [-@newpwfile]\n", progName); FPS "\t%s -S -n cert-name -s subj [-c issuer-name | -x] -t trustargs\n" "\t\t [-k key-type-or-id] [-q key-params] [-h token-name] [-g key-size]\n" "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n" "\t\t [-f pwfile] [-d certdir] [-P dbprefix] [-Z hashAlg]\n" "\t\t [-p phone] [-1] [-2] [-3] [-4] [-5] [-6] [-7 emailAddrs]\n" "\t\t [-8 DNS-names]\n" "\t\t [--extAIA] [--extSIA] [--extCP] [--extPM] [--extPC] [--extIA]\n" "\t\t [--extSKID] [--extNC] [--extSAN type:name[,type:name]...]\n" "\t\t [--extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...]\n", progName); FPS "\t%s -U [-X] [-d certdir] [-P dbprefix]\n", progName); exit(1); } enum usage_level { usage_all = 0, usage_selected = 1 }; static void luCommonDetailsAE(); static void luA(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "A")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Add a certificate to the database (create if needed)\n", "-A"); if (ul == usage_selected && !is_my_command) return; if (ul == usage_all) { FPS "%-20s\n", " All options under -E apply"); } else { luCommonDetailsAE(); } } static void luB(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "B")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Run a series of certutil commands from a batch file\n", "-B"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Specify the batch file\n", " -i batch-file"); } static void luE(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "E")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Add an Email certificate to the database (create if needed)\n", "-E"); if (ul == usage_selected && !is_my_command) return; luCommonDetailsAE(); } static void luCommonDetailsAE() { FPS "%-20s Specify the nickname of the certificate to add\n", " -n cert-name"); FPS "%-20s Set the certificate trust attributes:\n", " -t trustargs"); FPS "%-25s trustargs is of the form x,y,z where x is for SSL, y is for S/MIME,\n", ""); FPS "%-25s and z is for code signing. Use ,, for no explicit trust.\n", ""); FPS "%-25s p \t prohibited (explicitly distrusted)\n", ""); FPS "%-25s P \t trusted peer\n", ""); FPS "%-25s c \t valid CA\n", ""); FPS "%-25s T \t trusted CA to issue client certs (implies c)\n", ""); FPS "%-25s C \t trusted CA to issue server certs (implies c)\n", ""); FPS "%-25s u \t user cert\n", ""); FPS "%-25s w \t send warning\n", ""); FPS "%-25s g \t make step-up cert\n", ""); FPS "%-20s Specify the password file\n", " -f pwfile"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s The input certificate is encoded in ASCII (RFC1113)\n", " -a"); FPS "%-20s Specify the certificate file (default is stdin)\n", " -i input"); FPS "\n"); } static void luC(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "C")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Create a new binary certificate from a BINARY cert request\n", "-C"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The nickname of the issuer cert\n", " -c issuer-name"); FPS "%-20s The BINARY certificate request file\n", " -i cert-request "); FPS "%-20s Output binary cert to this file (default is stdout)\n", " -o output-cert"); FPS "%-20s Self sign\n", " -x"); FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n", " --pss-sign"); FPS "%-20s Cert serial number\n", " -m serial-number"); FPS "%-20s Time Warp\n", " -w warp-months"); FPS "%-20s Months valid (default is 3)\n", " -v months-valid"); FPS "%-20s Specify the password file\n", " -f pwfile"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s \n" "%-20s Specify the hash algorithm to use. Possible keywords:\n" "%-20s \"MD2\", \"MD4\", \"MD5\", \"SHA1\", \"SHA224\",\n" "%-20s \"SHA256\", \"SHA384\", \"SHA512\"\n", " -Z hashAlg", "", "", ""); FPS "%-20s \n" "%-20s Create key usage extension. Possible keywords:\n" "%-20s \"digitalSignature\", \"nonRepudiation\", \"keyEncipherment\",\n" "%-20s \"dataEncipherment\", \"keyAgreement\", \"certSigning\",\n" "%-20s \"crlSigning\", \"critical\"\n", " -1 | --keyUsage keyword,keyword,...", "", "", "", ""); FPS "%-20s Create basic constraint extension\n", " -2 "); FPS "%-20s Create authority key ID extension\n", " -3 "); FPS "%-20s Create crl distribution point extension\n", " -4 "); FPS "%-20s \n" "%-20s Create netscape cert type extension. Possible keywords:\n" "%-20s \"sslClient\", \"sslServer\", \"smime\", \"objectSigning\",\n" "%-20s \"sslCA\", \"smimeCA\", \"objectSigningCA\", \"critical\".\n", " -5 | --nsCertType keyword,keyword,... ", "", "", ""); FPS "%-20s \n" "%-20s Create extended key usage extension. Possible keywords:\n" "%-20s \"serverAuth\", \"clientAuth\",\"codeSigning\",\n" "%-20s \"emailProtection\", \"timeStamp\",\"ocspResponder\",\n" "%-20s \"stepUp\", \"msTrustListSign\", \"x509Any\",\n" "%-20s \"ipsecIKE\", \"ipsecIKEEnd\", \"ipsecIKEIntermediate\",\n" "%-20s \"ipsecEnd\", \"ipsecTunnel\", \"ipsecUser\",\n" "%-20s \"critical\"\n", " -6 | --extKeyUsage keyword,keyword,...", "", "", "", "", "", "", ""); FPS "%-20s Create an email subject alt name extension\n", " -7 emailAddrs"); FPS "%-20s Create an dns subject alt name extension\n", " -8 dnsNames"); FPS "%-20s The input certificate request is encoded in ASCII (RFC1113)\n", " -a"); FPS "\n"); } static void luG(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "G")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Generate a new key pair\n", "-G"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Name of token in which to generate key (default is internal)\n", " -h token-name"); FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n", " -k key-type"); FPS "%-20s Key size in bits, (min %d, max %d, default %d) (not for ec)\n", " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS); FPS "%-20s Set the public exponent value (3, 17, 65537) (rsa only)\n", " -y exp"); FPS "%-20s Specify the password file\n", " -f password-file"); FPS "%-20s Specify the noise file to be used\n", " -z noisefile"); FPS "%-20s read PQG value from pqgfile (dsa only)\n", " -q pqgfile"); FPS "%-20s Elliptic curve name (ec only)\n", " -q curve-name"); FPS "%-20s One of nistp256, nistp384, nistp521, curve25519.\n", ""); FPS "%-20s If a custom token is present, the following curves are also supported:\n", ""); FPS "%-20s sect163k1, nistk163, sect163r1, sect163r2,\n", ""); FPS "%-20s nistb163, sect193r1, sect193r2, sect233k1, nistk233,\n", ""); FPS "%-20s sect233r1, nistb233, sect239k1, sect283k1, nistk283,\n", ""); FPS "%-20s sect283r1, nistb283, sect409k1, nistk409, sect409r1,\n", ""); FPS "%-20s nistb409, sect571k1, nistk571, sect571r1, nistb571,\n", ""); FPS "%-20s secp160k1, secp160r1, secp160r2, secp192k1, secp192r1,\n", ""); FPS "%-20s nistp192, secp224k1, secp224r1, nistp224, secp256k1,\n", ""); FPS "%-20s secp256r1, secp384r1, secp521r1,\n", ""); FPS "%-20s prime192v1, prime192v2, prime192v3, \n", ""); FPS "%-20s prime239v1, prime239v2, prime239v3, c2pnb163v1, \n", ""); FPS "%-20s c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1, \n", ""); FPS "%-20s c2tnb191v2, c2tnb191v3, \n", ""); FPS "%-20s c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3, \n", ""); FPS "%-20s c2pnb272w1, c2pnb304w1, \n", ""); FPS "%-20s c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, \n", ""); FPS "%-20s secp112r2, secp128r1, secp128r2, sect113r1, sect113r2\n", ""); FPS "%-20s sect131r1, sect131r2\n", ""); FPS "%-20s Key database directory (default is ~/.netscape)\n", " -d keydir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s\n" "%-20s PKCS #11 key Attributes.\n", " --keyAttrFlags attrflags", ""); FPS "%-20s Comma separated list of key attribute attribute flags,\n", ""); FPS "%-20s selected from the following list of choices:\n", ""); FPS "%-20s {token | session} {public | private} {sensitive | insensitive}\n", ""); FPS "%-20s {modifiable | unmodifiable} {extractable | unextractable}\n", ""); FPS "%-20s\n", " --keyOpFlagsOn opflags"); FPS "%-20s\n" "%-20s PKCS #11 key Operation Flags.\n", " --keyOpFlagsOff opflags", ""); FPS "%-20s Comma separated list of one or more of the following:\n", ""); FPS "%-20s encrypt, decrypt, sign, sign_recover, verify,\n", ""); FPS "%-20s verify_recover, wrap, unwrap, derive\n", ""); FPS "\n"); } static void luD(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "D")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Delete a certificate from the database\n", "-D"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The nickname of the cert to delete\n", " -n cert-name"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "\n"); } static void luF(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "F")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Delete a key and associated certificate from the database\n", "-F"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The nickname of the key to delete\n", " -n cert-name"); FPS "%-20s The key id of the key to delete, obtained using -K\n", " -k key-id"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "\n"); } static void luU(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "U")); if (ul == usage_all || !command || is_my_command) FPS "%-15s List all modules\n", /*, or print out a single named module\n",*/ "-U"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Module database directory (default is '~/.netscape')\n", " -d moddir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s force the database to open R/W\n", " -X"); FPS "\n"); } static void luK(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "K")); if (ul == usage_all || !command || is_my_command) FPS "%-15s List all private keys\n", "-K"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Name of token to search (\"all\" for all tokens)\n", " -h token-name "); FPS "%-20s Key type (\"all\" (default), \"dsa\"," " \"ec\"," " \"rsa\")\n", " -k key-type"); FPS "%-20s The nickname of the key or associated certificate\n", " -n name"); FPS "%-20s Specify the password file\n", " -f password-file"); FPS "%-20s Key database directory (default is ~/.netscape)\n", " -d keydir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s force the database to open R/W\n", " -X"); FPS "\n"); } static void luL(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "L")); if (ul == usage_all || !command || is_my_command) FPS "%-15s List all certs, or print out a single named cert (or a subset)\n", "-L"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Name of token to search (\"all\" for all tokens)\n", " -h token-name "); FPS "%-20s Pretty print named cert (list all if unspecified)\n", " -n cert-name"); FPS "%-20s \n" "%-20s Pretty print cert with email address (list all if unspecified)\n", " --email email-address", ""); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s force the database to open R/W\n", " -X"); FPS "%-20s For single cert, print binary DER encoding\n", " -r"); FPS "%-20s For single cert, print ASCII encoding (RFC1113)\n", " -a"); FPS "%-20s \n" "%-20s For single cert, print binary DER encoding of extension OID\n", " --dump-ext-val OID", ""); FPS "\n"); } static void luM(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "M")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Modify trust attributes of certificate\n", "-M"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The nickname of the cert to modify\n", " -n cert-name"); FPS "%-20s Set the certificate trust attributes (see -A above)\n", " -t trustargs"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "\n"); } static void luN(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "N")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Create a new certificate database\n", "-N"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s Specify the password file\n", " -f password-file"); FPS "%-20s use empty password when creating a new database\n", " --empty-password"); FPS "\n"); } static void luT(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "T")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Reset the Key database or token\n", "-T"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s Token to reset (default is internal)\n", " -h token-name"); FPS "%-20s Set token's Site Security Officer password\n", " -0 SSO-password"); FPS "\n"); } static void luO(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "O")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Print the chain of a certificate\n", "-O"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The nickname of the cert to modify\n", " -n cert-name"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n", " -a"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s force the database to open R/W\n", " -X"); FPS "%-20s don't search for a chain if issuer name equals subject name\n", " --simple-self-signed"); FPS "\n"); } static void luR(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "R")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Generate a certificate request (stdout)\n", "-R"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Specify the subject name (using RFC1485)\n", " -s subject"); FPS "%-20s Output the cert request to this file\n", " -o output-req"); FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n", " -k key-type-or-id"); FPS "%-20s or nickname of the cert key to use, or key id obtained using -K\n", ""); FPS "%-20s Name of token in which to generate key (default is internal)\n", " -h token-name"); FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n", " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS); FPS "%-20s Create a certificate request restricted to RSA-PSS (rsa only)\n", " --pss"); FPS "%-20s Name of file containing PQG parameters (dsa only)\n", " -q pqgfile"); FPS "%-20s Elliptic curve name (ec only)\n", " -q curve-name"); FPS "%-20s See the \"-G\" option for a full list of supported names.\n", ""); FPS "%-20s Specify the password file\n", " -f pwfile"); FPS "%-20s Key database directory (default is ~/.netscape)\n", " -d keydir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n", " -p phone"); FPS "%-20s \n" "%-20s Specify the hash algorithm to use. Possible keywords:\n" "%-20s \"MD2\", \"MD4\", \"MD5\", \"SHA1\", \"SHA224\",\n" "%-20s \"SHA256\", \"SHA384\", \"SHA512\"\n", " -Z hashAlg", "", "", ""); FPS "%-20s Output the cert request in ASCII (RFC1113); default is binary\n", " -a"); FPS "%-20s \n", " See -S for available extension options"); FPS "%-20s \n", " See -G for available key flag options"); FPS "\n"); } static void luV(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "V")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Validate a certificate\n", "-V"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The nickname of the cert to Validate\n", " -n cert-name"); FPS "%-20s validity time (\"YYMMDDHHMMSS[+HHMM|-HHMM|Z]\")\n", " -b time"); FPS "%-20s Check certificate signature \n", " -e "); FPS "%-20s Specify certificate usage:\n", " -u certusage"); FPS "%-25s C \t SSL Client\n", ""); FPS "%-25s V \t SSL Server\n", ""); FPS "%-25s I \t IPsec\n", ""); FPS "%-25s L \t SSL CA\n", ""); FPS "%-25s A \t Any CA\n", ""); FPS "%-25s Y \t Verify CA\n", ""); FPS "%-25s S \t Email signer\n", ""); FPS "%-25s R \t Email Recipient\n", ""); FPS "%-25s O \t OCSP status responder\n", ""); FPS "%-25s J \t Object signer\n", ""); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n", " -a"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s force the database to open R/W\n", " -X"); FPS "\n"); } static void luW(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "W")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Change the key database password\n", "-W"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s cert and key database directory\n", " -d certdir"); FPS "%-20s Specify a file with the current password\n", " -f pwfile"); FPS "%-20s Specify a file with the new password in two lines\n", " -@ newpwfile"); FPS "\n"); } static void luRename(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "rename")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Change the database nickname of a certificate\n", "--rename"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s The old nickname of the cert to rename\n", " -n cert-name"); FPS "%-20s The new nickname of the cert to rename\n", " --new-n new-name"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "\n"); } static void luUpgradeMerge(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "upgrade-merge")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Upgrade an old database and merge it into a new one\n", "--upgrade-merge"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Cert database directory to merge into (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix of the target database\n", " -P dbprefix"); FPS "%-20s Specify the password file for the target database\n", " -f pwfile"); FPS "%-20s \n%-20s Cert database directory to upgrade from\n", " --source-dir certdir", ""); FPS "%-20s \n%-20s Cert & Key database prefix of the upgrade database\n", " --source-prefix dbprefix", ""); FPS "%-20s \n%-20s Unique identifier for the upgrade database\n", " --upgrade-id uniqueID", ""); FPS "%-20s \n%-20s Name of the token while it is in upgrade state\n", " --upgrade-token-name name", ""); FPS "%-20s Specify the password file for the upgrade database\n", " -@ pwfile"); FPS "\n"); } static void luMerge(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "merge")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Merge source database into the target database\n", "--merge"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Cert database directory of target (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix of the target database\n", " -P dbprefix"); FPS "%-20s Specify the password file for the target database\n", " -f pwfile"); FPS "%-20s \n%-20s Cert database directory of the source database\n", " --source-dir certdir", ""); FPS "%-20s \n%-20s Cert & Key database prefix of the source database\n", " --source-prefix dbprefix", ""); FPS "%-20s Specify the password file for the source database\n", " -@ pwfile"); FPS "\n"); } static void luS(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "S")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Make a certificate and add to database\n", "-S"); if (ul == usage_selected && !is_my_command) return; FPS "%-20s Specify the nickname of the cert\n", " -n key-name"); FPS "%-20s Specify the subject name (using RFC1485)\n", " -s subject"); FPS "%-20s The nickname of the issuer cert\n", " -c issuer-name"); FPS "%-20s Set the certificate trust attributes (see -A above)\n", " -t trustargs"); FPS "%-20s Type of key pair to generate (\"dsa\", \"ec\", \"rsa\" (default))\n", " -k key-type-or-id"); FPS "%-20s Name of token in which to generate key (default is internal)\n", " -h token-name"); FPS "%-20s Key size in bits, RSA keys only (min %d, max %d, default %d)\n", " -g key-size", MIN_KEY_BITS, MAX_KEY_BITS, DEFAULT_KEY_BITS); FPS "%-20s Create a certificate restricted to RSA-PSS (rsa only)\n", " --pss"); FPS "%-20s Name of file containing PQG parameters (dsa only)\n", " -q pqgfile"); FPS "%-20s Elliptic curve name (ec only)\n", " -q curve-name"); FPS "%-20s See the \"-G\" option for a full list of supported names.\n", ""); FPS "%-20s Self sign\n", " -x"); FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n", " --pss-sign"); FPS "%-20s Cert serial number\n", " -m serial-number"); FPS "%-20s Time Warp\n", " -w warp-months"); FPS "%-20s Months valid (default is 3)\n", " -v months-valid"); FPS "%-20s Specify the password file\n", " -f pwfile"); FPS "%-20s Cert database directory (default is ~/.netscape)\n", " -d certdir"); FPS "%-20s Cert & Key database prefix\n", " -P dbprefix"); FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n", " -p phone"); FPS "%-20s \n" "%-20s Specify the hash algorithm to use. Possible keywords:\n" "%-20s \"MD2\", \"MD4\", \"MD5\", \"SHA1\", \"SHA224\",\n" "%-20s \"SHA256\", \"SHA384\", \"SHA512\"\n", " -Z hashAlg", "", "", ""); FPS "%-20s Create key usage extension\n", " -1 "); FPS "%-20s Create basic constraint extension\n", " -2 "); FPS "%-20s Create authority key ID extension\n", " -3 "); FPS "%-20s Create crl distribution point extension\n", " -4 "); FPS "%-20s Create netscape cert type extension\n", " -5 "); FPS "%-20s Create extended key usage extension\n", " -6 "); FPS "%-20s Create an email subject alt name extension\n", " -7 emailAddrs "); FPS "%-20s Create a DNS subject alt name extension\n", " -8 DNS-names"); FPS "%-20s Create an Authority Information Access extension\n", " --extAIA "); FPS "%-20s Create a Subject Information Access extension\n", " --extSIA "); FPS "%-20s Create a Certificate Policies extension\n", " --extCP "); FPS "%-20s Create a Policy Mappings extension\n", " --extPM "); FPS "%-20s Create a Policy Constraints extension\n", " --extPC "); FPS "%-20s Create an Inhibit Any Policy extension\n", " --extIA "); FPS "%-20s Create a subject key ID extension\n", " --extSKID "); FPS "%-20s \n", " See -G for available key flag options"); FPS "%-20s Create a name constraints extension\n", " --extNC "); FPS "%-20s \n" "%-20s Create a Subject Alt Name extension with one or multiple names\n", " --extSAN type:name[,type:name]...", ""); FPS "%-20s - type: directory, dn, dns, edi, ediparty, email, ip, ipaddr,\n", ""); FPS "%-20s other, registerid, rfc822, uri, x400, x400addr\n", ""); FPS "%-20s \n" "%-20s Add one or multiple extensions that certutil cannot encode yet,\n" "%-20s by loading their encodings from external files.\n", " --extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...", "", ""); FPS "%-20s - OID (example): 1.2.3.4\n", ""); FPS "%-20s - critical-flag: critical or not-critical\n", ""); FPS "%-20s - filename: full path to a file containing an encoded extension\n", ""); FPS "\n"); } static void luBuildFlags(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "build-flags")); if (ul == usage_all || !command || is_my_command) FPS "%-15s Print enabled build flags relevant for NSS test execution\n", "--build-flags"); if (ul == usage_selected && !is_my_command) return; FPS "\n"); } static void LongUsage(enum usage_level ul, const char *command) { luA(ul, command); luB(ul, command); luE(ul, command); luC(ul, command); luG(ul, command); luD(ul, command); luRename(ul, command); luF(ul, command); luU(ul, command); luK(ul, command); luL(ul, command); luBuildFlags(ul, command); luM(ul, command); luN(ul, command); luT(ul, command); luO(ul, command); luR(ul, command); luV(ul, command); luW(ul, command); luUpgradeMerge(ul, command); luMerge(ul, command); luS(ul, command); #undef FPS } static void Usage() { PR_fprintf(PR_STDERR, "%s - Utility to manipulate NSS certificate databases\n\n" "Usage: %s -d \n\n" "Valid commands:\n", progName, progName); LongUsage(usage_selected, NULL); PR_fprintf(PR_STDERR, "\n" "%s -H : Print available options for the given command\n" "%s -H : Print complete help output of all commands and options\n" "%s --syntax : Print a short summary of all commands and options\n", progName, progName, progName); exit(1); } static CERTCertificate * MakeV1Cert(CERTCertDBHandle *handle, CERTCertificateRequest *req, char *issuerNickName, PRBool selfsign, unsigned int serialNumber, int warpmonths, int validityMonths) { CERTCertificate *issuerCert = NULL; CERTValidity *validity; CERTCertificate *cert = NULL; PRExplodedTime printableTime; PRTime now, after; if (!selfsign) { issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName); if (!issuerCert) { SECU_PrintError(progName, "could not find certificate named \"%s\"", issuerNickName); return NULL; } } now = PR_Now(); PR_ExplodeTime(now, PR_GMTParameters, &printableTime); if (warpmonths) { printableTime.tm_month += warpmonths; now = PR_ImplodeTime(&printableTime); PR_ExplodeTime(now, PR_GMTParameters, &printableTime); } printableTime.tm_month += validityMonths; after = PR_ImplodeTime(&printableTime); /* note that the time is now in micro-second unit */ validity = CERT_CreateValidity(now, after); if (validity) { cert = CERT_CreateCertificate(serialNumber, (selfsign ? &req->subject : &issuerCert->subject), validity, req); CERT_DestroyValidity(validity); } if (issuerCert) { CERT_DestroyCertificate(issuerCert); } return (cert); } static SECStatus SetSignatureAlgorithm(PLArenaPool *arena, SECAlgorithmID *signAlg, SECAlgorithmID *spkiAlg, SECOidTag hashAlgTag, SECKEYPrivateKey *privKey, PRBool pssSign) { SECStatus rv; if (pssSign || SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { SECItem *srcParams; SECItem *params; if (SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { srcParams = &spkiAlg->parameters; } else { /* If the issuer's public key is RSA, the parameter field * of the SPKI should be NULL, which can't be used as a * basis of RSA-PSS parameters. */ srcParams = NULL; } params = SEC_CreateSignatureAlgorithmParameters(arena, NULL, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlgTag, srcParams, privKey); if (!params) { SECU_PrintError(progName, "Could not create RSA-PSS parameters"); return SECFailure; } rv = SECOID_SetAlgorithmID(arena, signAlg, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params); if (rv != SECSuccess) { SECU_PrintError(progName, "Could not set signature algorithm id."); return rv; } } else { KeyType keyType = SECKEY_GetPrivateKeyType(privKey); SECOidTag algID; algID = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); if (algID == SEC_OID_UNKNOWN) { SECU_PrintError(progName, "Unknown key or hash type for issuer."); return SECFailure; } rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0); if (rv != SECSuccess) { SECU_PrintError(progName, "Could not set signature algorithm id."); return rv; } } return SECSuccess; } static SECStatus SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, SECOidTag hashAlgTag, SECKEYPrivateKey *privKey, char *issuerNickName, int certVersion, PRBool pssSign, void *pwarg) { SECItem der; SECKEYPrivateKey *caPrivateKey = NULL; SECStatus rv; PLArenaPool *arena; CERTCertificate *issuer; void *dummy; arena = cert->arena; if (selfsign) { issuer = cert; } else { issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); if ((CERTCertificate *)NULL == issuer) { SECU_PrintError(progName, "unable to find issuer with nickname %s", issuerNickName); rv = SECFailure; goto done; } privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); if (caPrivateKey == NULL) { SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName); rv = SECFailure; CERT_DestroyCertificate(issuer); goto done; } } if (pssSign && (SECKEY_GetPrivateKeyType(privKey) != rsaKey && SECKEY_GetPrivateKeyType(privKey) != rsaPssKey)) { SECU_PrintError(progName, "unable to create RSA-PSS signature with key %s", issuerNickName); rv = SECFailure; if (!selfsign) { CERT_DestroyCertificate(issuer); } goto done; } rv = SetSignatureAlgorithm(arena, &cert->signature, &issuer->subjectPublicKeyInfo.algorithm, hashAlgTag, privKey, pssSign); if (!selfsign) { CERT_DestroyCertificate(issuer); } if (rv != SECSuccess) { goto done; } switch (certVersion) { case (SEC_CERTIFICATE_VERSION_1): /* The initial version for x509 certificates is version one * and this default value must be an implicit DER encoding. */ cert->version.data = NULL; cert->version.len = 0; break; case (SEC_CERTIFICATE_VERSION_2): case (SEC_CERTIFICATE_VERSION_3): case 3: /* unspecified format (would be version 4 certificate). */ *(cert->version.data) = certVersion; cert->version.len = 1; break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; goto done; } der.len = 0; der.data = NULL; dummy = SEC_ASN1EncodeItem(arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate)); if (!dummy) { fprintf(stderr, "Could not encode certificate.\n"); rv = SECFailure; goto done; } rv = SEC_DerSignDataWithAlgorithmID(arena, &cert->derCert, der.data, der.len, privKey, &cert->signature); if (rv != SECSuccess) { fprintf(stderr, "Could not sign encoded certificate data.\n"); /* result allocated out of the arena, it will be freed * when the arena is freed */ goto done; } done: if (caPrivateKey) { SECKEY_DestroyPrivateKey(caPrivateKey); } return rv; } static SECStatus CreateCert( CERTCertDBHandle *handle, PK11SlotInfo *slot, char *issuerNickName, const SECItem *certReqDER, SECKEYPrivateKey **selfsignprivkey, void *pwarg, SECOidTag hashAlgTag, unsigned int serialNumber, int warpmonths, int validityMonths, const char *emailAddrs, const char *dnsNames, PRBool ascii, PRBool selfsign, certutilExtnList extnList, const char *extGeneric, int certVersion, PRBool pssSign, SECItem *certDER) { void *extHandle = NULL; CERTCertificate *subjectCert = NULL; CERTCertificateRequest *certReq = NULL; SECStatus rv = SECSuccess; CERTCertExtension **CRexts; do { /* Create a certrequest object from the input cert request der */ certReq = GetCertRequest(certReqDER, pwarg); if (certReq == NULL) { GEN_BREAK(SECFailure) } subjectCert = MakeV1Cert(handle, certReq, issuerNickName, selfsign, serialNumber, warpmonths, validityMonths); if (subjectCert == NULL) { GEN_BREAK(SECFailure) } extHandle = CERT_StartCertExtensions(subjectCert); if (extHandle == NULL) { GEN_BREAK(SECFailure) } rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric); if (rv != SECSuccess) { GEN_BREAK(SECFailure) } if (certReq->attributes != NULL && certReq->attributes[0] != NULL && certReq->attributes[0]->attrType.data != NULL && certReq->attributes[0]->attrType.len > 0 && SECOID_FindOIDTag(&certReq->attributes[0]->attrType) == SEC_OID_PKCS9_EXTENSION_REQUEST) { rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts); if (rv != SECSuccess) break; rv = CERT_MergeExtensions(extHandle, CRexts); if (rv != SECSuccess) break; } CERT_FinishExtensions(extHandle); extHandle = NULL; /* self-signing a cert request, find the private key */ if (selfsign && *selfsignprivkey == NULL) { *selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg); if (!*selfsignprivkey) { fprintf(stderr, "Failed to locate private key.\n"); rv = SECFailure; break; } } rv = SignCert(handle, subjectCert, selfsign, hashAlgTag, *selfsignprivkey, issuerNickName, certVersion, pssSign, pwarg); if (rv != SECSuccess) break; rv = SECFailure; if (ascii) { char *asciiDER = BTOA_DataToAscii(subjectCert->derCert.data, subjectCert->derCert.len); if (asciiDER) { char *wrapped = PR_smprintf("%s\n%s\n%s\n", NS_CERT_HEADER, asciiDER, NS_CERT_TRAILER); if (wrapped) { PRUint32 wrappedLen = PL_strlen(wrapped); if (SECITEM_AllocItem(NULL, certDER, wrappedLen)) { PORT_Memcpy(certDER->data, wrapped, wrappedLen); rv = SECSuccess; } PR_smprintf_free(wrapped); } PORT_Free(asciiDER); } } else { rv = SECITEM_CopyItem(NULL, certDER, &subjectCert->derCert); } } while (0); if (extHandle) { CERT_FinishExtensions(extHandle); } CERT_DestroyCertificateRequest(certReq); CERT_DestroyCertificate(subjectCert); if (rv != SECSuccess) { PRErrorCode perr = PR_GetError(); fprintf(stderr, "%s: unable to create cert (%s)\n", progName, SECU_Strerror(perr)); } return (rv); } /* * map a class to a user presentable string */ static const char *objClassArray[] = { "Data", "Certificate", "Public Key", "Private Key", "Secret Key", "Hardware Feature", "Domain Parameters", "Mechanism" }; static const char *objNSSClassArray[] = { "CKO_NSS", "Crl", "SMIME Record", "Trust", "Builtin Root List" }; const char * getObjectClass(CK_ULONG classType) { static char buf[sizeof(CK_ULONG) * 2 + 3]; if (classType <= CKO_MECHANISM) { return objClassArray[classType]; } if (classType >= CKO_NSS && classType <= CKO_NSS_BUILTIN_ROOT_LIST) { return objNSSClassArray[classType - CKO_NSS]; } snprintf(buf, sizeof(buf), "0x%lx", classType); return buf; } typedef struct { char *name; int nameSize; CK_ULONG value; } flagArray; #define NAME_SIZE(x) #x, sizeof(#x) - 1 flagArray opFlagsArray[] = { { NAME_SIZE(encrypt), CKF_ENCRYPT }, { NAME_SIZE(decrypt), CKF_DECRYPT }, { NAME_SIZE(sign), CKF_SIGN }, { NAME_SIZE(sign_recover), CKF_SIGN_RECOVER }, { NAME_SIZE(verify), CKF_VERIFY }, { NAME_SIZE(verify_recover), CKF_VERIFY_RECOVER }, { NAME_SIZE(wrap), CKF_WRAP }, { NAME_SIZE(unwrap), CKF_UNWRAP }, { NAME_SIZE(derive), CKF_DERIVE } }; int opFlagsCount = PR_ARRAY_SIZE(opFlagsArray); flagArray attrFlagsArray[] = { { NAME_SIZE(token), PK11_ATTR_TOKEN }, { NAME_SIZE(session), PK11_ATTR_SESSION }, { NAME_SIZE(private), PK11_ATTR_PRIVATE }, { NAME_SIZE(public), PK11_ATTR_PUBLIC }, { NAME_SIZE(modifiable), PK11_ATTR_MODIFIABLE }, { NAME_SIZE(unmodifiable), PK11_ATTR_UNMODIFIABLE }, { NAME_SIZE(sensitive), PK11_ATTR_SENSITIVE }, { NAME_SIZE(insensitive), PK11_ATTR_INSENSITIVE }, { NAME_SIZE(extractable), PK11_ATTR_EXTRACTABLE }, { NAME_SIZE(unextractable), PK11_ATTR_UNEXTRACTABLE } }; int attrFlagsCount = PR_ARRAY_SIZE(attrFlagsArray); #define MAX_STRING 30 CK_ULONG GetFlags(char *flagsString, flagArray *flags, int count) { CK_ULONG flagsValue = strtol(flagsString, NULL, 0); int i; if ((flagsValue != 0) || (*flagsString == 0)) { return flagsValue; } while (*flagsString) { for (i = 0; i < count; i++) { if (strncmp(flagsString, flags[i].name, flags[i].nameSize) == 0) { flagsValue |= flags[i].value; flagsString += flags[i].nameSize; if (*flagsString != 0) { flagsString++; } break; } } if (i == count) { char name[MAX_STRING]; char *tok; strncpy(name, flagsString, MAX_STRING); name[MAX_STRING - 1] = 0; tok = strchr(name, ','); if (tok) { *tok = 0; } fprintf(stderr, "Unknown flag (%s)\n", name); tok = strchr(flagsString, ','); if (tok == NULL) { break; } flagsString = tok + 1; } } return flagsValue; } CK_FLAGS GetOpFlags(char *flags) { return GetFlags(flags, opFlagsArray, opFlagsCount); } PK11AttrFlags GetAttrFlags(char *flags) { return GetFlags(flags, attrFlagsArray, attrFlagsCount); } char * mkNickname(unsigned char *data, int len) { char *nick = PORT_Alloc(len + 1); if (!nick) { return nick; } PORT_Memcpy(nick, data, len); nick[len] = 0; return nick; } /* * dump a PK11_MergeTokens error log to the console */ void DumpMergeLog(const char *progname, PK11MergeLog *log) { PK11MergeLogNode *node; for (node = log->head; node; node = node->next) { SECItem attrItem; char *nickname = NULL; const char *objectClass = NULL; SECStatus rv; attrItem.data = NULL; rv = PK11_ReadRawAttribute(PK11_TypeGeneric, node->object, CKA_LABEL, &attrItem); if (rv == SECSuccess) { nickname = mkNickname(attrItem.data, attrItem.len); PORT_Free(attrItem.data); } attrItem.data = NULL; rv = PK11_ReadRawAttribute(PK11_TypeGeneric, node->object, CKA_CLASS, &attrItem); if (rv == SECSuccess) { if (attrItem.len == sizeof(CK_ULONG)) { objectClass = getObjectClass(*(CK_ULONG *)attrItem.data); } PORT_Free(attrItem.data); } fprintf(stderr, "%s: Could not merge object %s (type %s): %s\n", progName, nickname ? nickname : "unnamed", objectClass ? objectClass : "unknown", SECU_Strerror(node->error)); if (nickname) { PORT_Free(nickname); } } } /* Certutil commands */ enum { cmd_AddCert = 0, cmd_CreateNewCert, cmd_DeleteCert, cmd_AddEmailCert, cmd_DeleteKey, cmd_GenKeyPair, cmd_PrintHelp, cmd_PrintSyntax, cmd_ListKeys, cmd_ListCerts, cmd_ModifyCertTrust, cmd_NewDBs, cmd_DumpChain, cmd_CertReq, cmd_CreateAndAddCert, cmd_TokenReset, cmd_ListModules, cmd_CheckCertValidity, cmd_ChangePassword, cmd_Version, cmd_Batch, cmd_Merge, cmd_UpgradeMerge, /* test only */ cmd_Rename, cmd_BuildFlags, max_cmd }; /* Certutil options */ enum certutilOpts { opt_SSOPass = 0, opt_AddKeyUsageExt, opt_AddBasicConstraintExt, opt_AddAuthorityKeyIDExt, opt_AddCRLDistPtsExt, opt_AddNSCertTypeExt, opt_AddExtKeyUsageExt, opt_ExtendedEmailAddrs, opt_ExtendedDNSNames, opt_ASCIIForIO, opt_ValidityTime, opt_IssuerName, opt_CertDir, opt_VerifySig, opt_PasswordFile, opt_KeySize, opt_TokenName, opt_InputFile, opt_Emailaddress, opt_KeyIndex, opt_KeyType, opt_DetailedInfo, opt_SerialNumber, opt_Nickname, opt_OutputFile, opt_PhoneNumber, opt_DBPrefix, opt_PQGFile, opt_BinaryDER, opt_Subject, opt_Trust, opt_Usage, opt_Validity, opt_OffsetMonths, opt_SelfSign, opt_RW, opt_Exponent, opt_NoiseFile, opt_Hash, opt_NewPasswordFile, opt_AddAuthInfoAccExt, opt_AddSubjInfoAccExt, opt_AddCertPoliciesExt, opt_AddPolicyMapExt, opt_AddPolicyConstrExt, opt_AddInhibAnyExt, opt_AddNameConstraintsExt, opt_AddSubjectKeyIDExt, opt_AddCmdKeyUsageExt, opt_AddCmdNSCertTypeExt, opt_AddCmdExtKeyUsageExt, opt_SourceDir, opt_SourcePrefix, opt_UpgradeID, opt_UpgradeTokenName, opt_KeyOpFlagsOn, opt_KeyOpFlagsOff, opt_KeyAttrFlags, opt_EmptyPassword, opt_CertVersion, opt_AddSubjectAltNameExt, opt_DumpExtensionValue, opt_GenericExtensions, opt_NewNickname, opt_Pss, opt_PssSign, opt_SimpleSelfSigned, opt_Help }; static const secuCommandFlag commands_init[] = { { /* cmd_AddCert */ 'A', PR_FALSE, 0, PR_FALSE }, { /* cmd_CreateNewCert */ 'C', PR_FALSE, 0, PR_FALSE }, { /* cmd_DeleteCert */ 'D', PR_FALSE, 0, PR_FALSE }, { /* cmd_AddEmailCert */ 'E', PR_FALSE, 0, PR_FALSE }, { /* cmd_DeleteKey */ 'F', PR_FALSE, 0, PR_FALSE }, { /* cmd_GenKeyPair */ 'G', PR_FALSE, 0, PR_FALSE }, { /* cmd_PrintHelp */ 'H', PR_FALSE, 0, PR_FALSE, "help" }, { /* cmd_PrintSyntax */ 0, PR_FALSE, 0, PR_FALSE, "syntax" }, { /* cmd_ListKeys */ 'K', PR_FALSE, 0, PR_FALSE }, { /* cmd_ListCerts */ 'L', PR_FALSE, 0, PR_FALSE }, { /* cmd_ModifyCertTrust */ 'M', PR_FALSE, 0, PR_FALSE }, { /* cmd_NewDBs */ 'N', PR_FALSE, 0, PR_FALSE }, { /* cmd_DumpChain */ 'O', PR_FALSE, 0, PR_FALSE }, { /* cmd_CertReq */ 'R', PR_FALSE, 0, PR_FALSE }, { /* cmd_CreateAndAddCert */ 'S', PR_FALSE, 0, PR_FALSE }, { /* cmd_TokenReset */ 'T', PR_FALSE, 0, PR_FALSE }, { /* cmd_ListModules */ 'U', PR_FALSE, 0, PR_FALSE }, { /* cmd_CheckCertValidity */ 'V', PR_FALSE, 0, PR_FALSE }, { /* cmd_ChangePassword */ 'W', PR_FALSE, 0, PR_FALSE }, { /* cmd_Version */ 'Y', PR_FALSE, 0, PR_FALSE }, { /* cmd_Batch */ 'B', PR_FALSE, 0, PR_FALSE }, { /* cmd_Merge */ 0, PR_FALSE, 0, PR_FALSE, "merge" }, { /* cmd_UpgradeMerge */ 0, PR_FALSE, 0, PR_FALSE, "upgrade-merge" }, { /* cmd_Rename */ 0, PR_FALSE, 0, PR_FALSE, "rename" }, { /* cmd_BuildFlags */ 0, PR_FALSE, 0, PR_FALSE, "build-flags" } }; #define NUM_COMMANDS ((sizeof commands_init) / (sizeof commands_init[0])) static const secuCommandFlag options_init[] = { { /* opt_SSOPass */ '0', PR_TRUE, 0, PR_FALSE }, { /* opt_AddKeyUsageExt */ '1', PR_FALSE, 0, PR_FALSE }, { /* opt_AddBasicConstraintExt*/ '2', PR_FALSE, 0, PR_FALSE }, { /* opt_AddAuthorityKeyIDExt*/ '3', PR_FALSE, 0, PR_FALSE }, { /* opt_AddCRLDistPtsExt */ '4', PR_FALSE, 0, PR_FALSE }, { /* opt_AddNSCertTypeExt */ '5', PR_FALSE, 0, PR_FALSE }, { /* opt_AddExtKeyUsageExt */ '6', PR_FALSE, 0, PR_FALSE }, { /* opt_ExtendedEmailAddrs */ '7', PR_TRUE, 0, PR_FALSE }, { /* opt_ExtendedDNSNames */ '8', PR_TRUE, 0, PR_FALSE }, { /* opt_ASCIIForIO */ 'a', PR_FALSE, 0, PR_FALSE }, { /* opt_ValidityTime */ 'b', PR_TRUE, 0, PR_FALSE }, { /* opt_IssuerName */ 'c', PR_TRUE, 0, PR_FALSE }, { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, { /* opt_VerifySig */ 'e', PR_FALSE, 0, PR_FALSE }, { /* opt_PasswordFile */ 'f', PR_TRUE, 0, PR_FALSE }, { /* opt_KeySize */ 'g', PR_TRUE, 0, PR_FALSE }, { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE }, { /* opt_InputFile */ 'i', PR_TRUE, 0, PR_FALSE }, { /* opt_Emailaddress */ 0, PR_TRUE, 0, PR_FALSE, "email" }, { /* opt_KeyIndex */ 'j', PR_TRUE, 0, PR_FALSE }, { /* opt_KeyType */ 'k', PR_TRUE, 0, PR_FALSE }, { /* opt_DetailedInfo */ 'l', PR_FALSE, 0, PR_FALSE }, { /* opt_SerialNumber */ 'm', PR_TRUE, 0, PR_FALSE }, { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE }, { /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE }, { /* opt_PhoneNumber */ 'p', PR_TRUE, 0, PR_FALSE }, { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE }, { /* opt_PQGFile */ 'q', PR_TRUE, 0, PR_FALSE }, { /* opt_BinaryDER */ 'r', PR_FALSE, 0, PR_FALSE }, { /* opt_Subject */ 's', PR_TRUE, 0, PR_FALSE }, { /* opt_Trust */ 't', PR_TRUE, 0, PR_FALSE }, { /* opt_Usage */ 'u', PR_TRUE, 0, PR_FALSE }, { /* opt_Validity */ 'v', PR_TRUE, 0, PR_FALSE }, { /* opt_OffsetMonths */ 'w', PR_TRUE, 0, PR_FALSE }, { /* opt_SelfSign */ 'x', PR_FALSE, 0, PR_FALSE }, { /* opt_RW */ 'X', PR_FALSE, 0, PR_FALSE }, { /* opt_Exponent */ 'y', PR_TRUE, 0, PR_FALSE }, { /* opt_NoiseFile */ 'z', PR_TRUE, 0, PR_FALSE }, { /* opt_Hash */ 'Z', PR_TRUE, 0, PR_FALSE }, { /* opt_NewPasswordFile */ '@', PR_TRUE, 0, PR_FALSE }, { /* opt_AddAuthInfoAccExt */ 0, PR_FALSE, 0, PR_FALSE, "extAIA" }, { /* opt_AddSubjInfoAccExt */ 0, PR_FALSE, 0, PR_FALSE, "extSIA" }, { /* opt_AddCertPoliciesExt */ 0, PR_FALSE, 0, PR_FALSE, "extCP" }, { /* opt_AddPolicyMapExt */ 0, PR_FALSE, 0, PR_FALSE, "extPM" }, { /* opt_AddPolicyConstrExt */ 0, PR_FALSE, 0, PR_FALSE, "extPC" }, { /* opt_AddInhibAnyExt */ 0, PR_FALSE, 0, PR_FALSE, "extIA" }, { /* opt_AddNameConstraintsExt*/ 0, PR_FALSE, 0, PR_FALSE, "extNC" }, { /* opt_AddSubjectKeyIDExt */ 0, PR_FALSE, 0, PR_FALSE, "extSKID" }, { /* opt_AddCmdKeyUsageExt */ 0, PR_TRUE, 0, PR_FALSE, "keyUsage" }, { /* opt_AddCmdNSCertTypeExt */ 0, PR_TRUE, 0, PR_FALSE, "nsCertType" }, { /* opt_AddCmdExtKeyUsageExt*/ 0, PR_TRUE, 0, PR_FALSE, "extKeyUsage" }, { /* opt_SourceDir */ 0, PR_TRUE, 0, PR_FALSE, "source-dir" }, { /* opt_SourcePrefix */ 0, PR_TRUE, 0, PR_FALSE, "source-prefix" }, { /* opt_UpgradeID */ 0, PR_TRUE, 0, PR_FALSE, "upgrade-id" }, { /* opt_UpgradeTokenName */ 0, PR_TRUE, 0, PR_FALSE, "upgrade-token-name" }, { /* opt_KeyOpFlagsOn */ 0, PR_TRUE, 0, PR_FALSE, "keyOpFlagsOn" }, { /* opt_KeyOpFlagsOff */ 0, PR_TRUE, 0, PR_FALSE, "keyOpFlagsOff" }, { /* opt_KeyAttrFlags */ 0, PR_TRUE, 0, PR_FALSE, "keyAttrFlags" }, { /* opt_EmptyPassword */ 0, PR_FALSE, 0, PR_FALSE, "empty-password" }, { /* opt_CertVersion */ 0, PR_TRUE, 0, PR_FALSE, "certVersion" }, { /* opt_AddSubjectAltExt */ 0, PR_TRUE, 0, PR_FALSE, "extSAN" }, { /* opt_DumpExtensionValue */ 0, PR_TRUE, 0, PR_FALSE, "dump-ext-val" }, { /* opt_GenericExtensions */ 0, PR_TRUE, 0, PR_FALSE, "extGeneric" }, { /* opt_NewNickname */ 0, PR_TRUE, 0, PR_FALSE, "new-n" }, { /* opt_Pss */ 0, PR_FALSE, 0, PR_FALSE, "pss" }, { /* opt_PssSign */ 0, PR_FALSE, 0, PR_FALSE, "pss-sign" }, { /* opt_SimpleSelfSigned */ 0, PR_FALSE, 0, PR_FALSE, "simple-self-signed" }, }; #define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0])) static secuCommandFlag certutil_commands[NUM_COMMANDS]; static secuCommandFlag certutil_options[NUM_OPTIONS]; static const secuCommand certutil = { NUM_COMMANDS, NUM_OPTIONS, certutil_commands, certutil_options }; static certutilExtnList certutil_extns; static int certutil_main(int argc, char **argv, PRBool initialize) { CERTCertDBHandle *certHandle; PK11SlotInfo *slot = NULL; CERTName *subject = 0; PRFileDesc *inFile = PR_STDIN; PRFileDesc *outFile = PR_STDOUT; SECItem certReqDER = { siBuffer, NULL, 0 }; SECItem certDER = { siBuffer, NULL, 0 }; const char *slotname = "internal"; const char *certPrefix = ""; char *sourceDir = ""; const char *srcCertPrefix = ""; char *upgradeID = ""; char *upgradeTokenName = ""; KeyType keytype = rsaKey; char *name = NULL; char *newName = NULL; char *email = NULL; char *keysource = NULL; SECOidTag hashAlgTag = SEC_OID_UNKNOWN; int keysize = DEFAULT_KEY_BITS; int publicExponent = 0x010001; int certVersion = SEC_CERTIFICATE_VERSION_3; unsigned int serialNumber = 0; int warpmonths = 0; int validityMonths = 3; int commandsEntered = 0; char commandToRun = '\0'; secuPWData pwdata = { PW_NONE, 0 }; secuPWData pwdata2 = { PW_NONE, 0 }; PRBool readOnly = PR_FALSE; PRBool initialized = PR_FALSE; CK_FLAGS keyOpFlagsOn = 0; CK_FLAGS keyOpFlagsOff = 0; PK11AttrFlags keyAttrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE; SECKEYPrivateKey *privkey = NULL; SECKEYPublicKey *pubkey = NULL; int i; SECStatus rv; progName = PORT_Strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; memcpy(certutil_commands, commands_init, sizeof commands_init); memcpy(certutil_options, options_init, sizeof options_init); rv = SECU_ParseCommandLine(argc, argv, progName, &certutil); if (rv != SECSuccess) Usage(); if (certutil.commands[cmd_PrintSyntax].activated) { PrintSyntax(); } if (certutil.commands[cmd_PrintHelp].activated) { char buf[2]; const char *command = NULL; for (i = 0; i < max_cmd; i++) { if (i == cmd_PrintHelp) continue; if (certutil.commands[i].activated) { if (certutil.commands[i].flag) { buf[0] = certutil.commands[i].flag; buf[1] = 0; command = buf; } else { command = certutil.commands[i].longform; } break; } } LongUsage((command ? usage_selected : usage_all), command); exit(1); } if (certutil.commands[cmd_BuildFlags].activated) { PrintBuildFlags(); } if (certutil.options[opt_PasswordFile].arg) { pwdata.source = PW_FROMFILE; pwdata.data = certutil.options[opt_PasswordFile].arg; } if (certutil.options[opt_NewPasswordFile].arg) { pwdata2.source = PW_FROMFILE; pwdata2.data = certutil.options[opt_NewPasswordFile].arg; } if (certutil.options[opt_CertDir].activated) SECU_ConfigDirectory(certutil.options[opt_CertDir].arg); if (certutil.options[opt_SourceDir].activated) sourceDir = certutil.options[opt_SourceDir].arg; if (certutil.options[opt_UpgradeID].activated) upgradeID = certutil.options[opt_UpgradeID].arg; if (certutil.options[opt_UpgradeTokenName].activated) upgradeTokenName = certutil.options[opt_UpgradeTokenName].arg; if (certutil.options[opt_KeySize].activated) { keysize = PORT_Atoi(certutil.options[opt_KeySize].arg); if ((keysize < MIN_KEY_BITS) || (keysize > MAX_KEY_BITS)) { PR_fprintf(PR_STDERR, "%s -g: Keysize must be between %d and %d.\n", progName, MIN_KEY_BITS, MAX_KEY_BITS); return 255; } if (keytype == ecKey) { PR_fprintf(PR_STDERR, "%s -g: Not for ec keys.\n", progName); return 255; } } /* -h specify token name */ if (certutil.options[opt_TokenName].activated) { if (PL_strcmp(certutil.options[opt_TokenName].arg, "all") == 0) slotname = NULL; else slotname = certutil.options[opt_TokenName].arg; } /* -Z hash type */ if (certutil.options[opt_Hash].activated) { char *arg = certutil.options[opt_Hash].arg; hashAlgTag = SECU_StringToSignatureAlgTag(arg); if (hashAlgTag == SEC_OID_UNKNOWN) { PR_fprintf(PR_STDERR, "%s -Z: %s is not a recognized type.\n", progName, arg); return 255; } } /* -k key type */ if (certutil.options[opt_KeyType].activated) { char *arg = certutil.options[opt_KeyType].arg; if (PL_strcmp(arg, "rsa") == 0) { keytype = rsaKey; } else if (PL_strcmp(arg, "dsa") == 0) { keytype = dsaKey; } else if (PL_strcmp(arg, "ec") == 0) { keytype = ecKey; } else if (PL_strcmp(arg, "all") == 0) { keytype = nullKey; } else { /* use an existing private/public key pair */ keysource = arg; } } else if (certutil.commands[cmd_ListKeys].activated) { keytype = nullKey; } if (certutil.options[opt_KeyOpFlagsOn].activated) { keyOpFlagsOn = GetOpFlags(certutil.options[opt_KeyOpFlagsOn].arg); } if (certutil.options[opt_KeyOpFlagsOff].activated) { keyOpFlagsOff = GetOpFlags(certutil.options[opt_KeyOpFlagsOff].arg); keyOpFlagsOn &= ~keyOpFlagsOff; /* make off override on */ } if (certutil.options[opt_KeyAttrFlags].activated) { keyAttrFlags = GetAttrFlags(certutil.options[opt_KeyAttrFlags].arg); } /* -m serial number */ if (certutil.options[opt_SerialNumber].activated) { int sn = PORT_Atoi(certutil.options[opt_SerialNumber].arg); if (sn < 0) { PR_fprintf(PR_STDERR, "%s -m: %s is not a valid serial number.\n", progName, certutil.options[opt_SerialNumber].arg); return 255; } serialNumber = sn; } /* -P certdb name prefix */ if (certutil.options[opt_DBPrefix].activated) { if (certutil.options[opt_DBPrefix].arg) { certPrefix = certutil.options[opt_DBPrefix].arg; } else { Usage(); } } /* --source-prefix certdb name prefix */ if (certutil.options[opt_SourcePrefix].activated) { if (certutil.options[opt_SourcePrefix].arg) { srcCertPrefix = certutil.options[opt_SourcePrefix].arg; } else { Usage(); } } /* -q PQG file or curve name */ if (certutil.options[opt_PQGFile].activated) { if ((keytype != dsaKey) && (keytype != ecKey)) { PR_fprintf(PR_STDERR, "%s -q: specifies a PQG file for DSA keys" " (-k dsa) or a named curve for EC keys (-k ec)\n)", progName); return 255; } } /* -s subject name */ if (certutil.options[opt_Subject].activated) { subject = CERT_AsciiToName(certutil.options[opt_Subject].arg); if (!subject) { PR_fprintf(PR_STDERR, "%s -s: improperly formatted name: \"%s\"\n", progName, certutil.options[opt_Subject].arg); return 255; } } /* -v validity period */ if (certutil.options[opt_Validity].activated) { validityMonths = PORT_Atoi(certutil.options[opt_Validity].arg); if (validityMonths < 0) { PR_fprintf(PR_STDERR, "%s -v: incorrect validity period: \"%s\"\n", progName, certutil.options[opt_Validity].arg); return 255; } } /* -w warp months */ if (certutil.options[opt_OffsetMonths].activated) warpmonths = PORT_Atoi(certutil.options[opt_OffsetMonths].arg); /* -y public exponent (for RSA) */ if (certutil.options[opt_Exponent].activated) { publicExponent = PORT_Atoi(certutil.options[opt_Exponent].arg); if ((publicExponent != 3) && (publicExponent != 17) && (publicExponent != 65537)) { PR_fprintf(PR_STDERR, "%s -y: incorrect public exponent %d.", progName, publicExponent); PR_fprintf(PR_STDERR, "Must be 3, 17, or 65537.\n"); return 255; } } /* --certVersion */ if (certutil.options[opt_CertVersion].activated) { certVersion = PORT_Atoi(certutil.options[opt_CertVersion].arg); if (certVersion < 1 || certVersion > 4) { PR_fprintf(PR_STDERR, "%s -certVersion: incorrect certificate version %d.", progName, certVersion); PR_fprintf(PR_STDERR, "Must be 1, 2, 3 or 4.\n"); return 255; } certVersion = certVersion - 1; } /* Check number of commands entered. */ commandsEntered = 0; for (i = 0; i < certutil.numCommands; i++) { if (certutil.commands[i].activated) { commandToRun = certutil.commands[i].flag; commandsEntered++; } if (commandsEntered > 1) break; } if (commandsEntered > 1) { PR_fprintf(PR_STDERR, "%s: only one command at a time!\n", progName); PR_fprintf(PR_STDERR, "You entered: "); for (i = 0; i < certutil.numCommands; i++) { if (certutil.commands[i].activated) PR_fprintf(PR_STDERR, " -%c", certutil.commands[i].flag); } PR_fprintf(PR_STDERR, "\n"); return 255; } if (commandsEntered == 0) { Usage(); } if (certutil.commands[cmd_ListCerts].activated || certutil.commands[cmd_PrintHelp].activated || certutil.commands[cmd_ListKeys].activated || certutil.commands[cmd_ListModules].activated || certutil.commands[cmd_CheckCertValidity].activated || certutil.commands[cmd_Version].activated) { readOnly = !certutil.options[opt_RW].activated; } /* -A, -D, -M, -S, -V, and all require -n */ if ((certutil.commands[cmd_AddCert].activated || certutil.commands[cmd_DeleteCert].activated || certutil.commands[cmd_DumpChain].activated || certutil.commands[cmd_ModifyCertTrust].activated || certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_CheckCertValidity].activated) && !certutil.options[opt_Nickname].activated) { PR_fprintf(PR_STDERR, "%s -%c: nickname is required for this command (-n).\n", progName, commandToRun); return 255; } /* -A, -E, -M, -S require trust */ if ((certutil.commands[cmd_AddCert].activated || certutil.commands[cmd_AddEmailCert].activated || certutil.commands[cmd_ModifyCertTrust].activated || certutil.commands[cmd_CreateAndAddCert].activated) && !certutil.options[opt_Trust].activated) { PR_fprintf(PR_STDERR, "%s -%c: trust is required for this command (-t).\n", progName, commandToRun); return 255; } /* if -L is given raw, ascii or dump mode, it must be for only one cert. */ if (certutil.commands[cmd_ListCerts].activated && (certutil.options[opt_ASCIIForIO].activated || certutil.options[opt_DumpExtensionValue].activated || certutil.options[opt_BinaryDER].activated) && !certutil.options[opt_Nickname].activated) { PR_fprintf(PR_STDERR, "%s: nickname is required to dump cert in raw or ascii mode.\n", progName); return 255; } /* -L can only be in (raw || ascii). */ if (certutil.commands[cmd_ListCerts].activated && certutil.options[opt_ASCIIForIO].activated && certutil.options[opt_BinaryDER].activated) { PR_fprintf(PR_STDERR, "%s: cannot specify both -r and -a when dumping cert.\n", progName); return 255; } /* If making a cert request, need a subject. */ if ((certutil.commands[cmd_CertReq].activated || certutil.commands[cmd_CreateAndAddCert].activated) && !(certutil.options[opt_Subject].activated || keysource)) { PR_fprintf(PR_STDERR, "%s -%c: subject is required to create a cert request.\n", progName, commandToRun); return 255; } /* If making a cert, need a serial number. */ if ((certutil.commands[cmd_CreateNewCert].activated || certutil.commands[cmd_CreateAndAddCert].activated) && !certutil.options[opt_SerialNumber].activated) { /* Make a default serial number from the current time. */ PRTime now = PR_Now(); LL_USHR(now, now, 19); LL_L2UI(serialNumber, now); } /* Validation needs the usage to validate for. */ if (certutil.commands[cmd_CheckCertValidity].activated && !certutil.options[opt_Usage].activated) { PR_fprintf(PR_STDERR, "%s -V: specify a usage to validate the cert for (-u).\n", progName); return 255; } /* Rename needs an old and a new nickname */ if (certutil.commands[cmd_Rename].activated && !(certutil.options[opt_Nickname].activated && certutil.options[opt_NewNickname].activated)) { PR_fprintf(PR_STDERR, "%s --rename: specify an old nickname (-n) and\n" " a new nickname (--new-n).\n", progName); return 255; } /* Delete needs a nickname or a key ID */ if (certutil.commands[cmd_DeleteKey].activated && !(certutil.options[opt_Nickname].activated || keysource)) { PR_fprintf(PR_STDERR, "%s -%c: specify a nickname (-n) or\n" " a key ID (-k).\n", progName, commandToRun); return 255; } /* Upgrade/Merge needs a source database and a upgrade id. */ if (certutil.commands[cmd_UpgradeMerge].activated && !(certutil.options[opt_SourceDir].activated && certutil.options[opt_UpgradeID].activated)) { PR_fprintf(PR_STDERR, "%s --upgrade-merge: specify an upgrade database directory " "(--source-dir) and\n" " an upgrade ID (--upgrade-id).\n", progName); return 255; } /* Merge needs a source database */ if (certutil.commands[cmd_Merge].activated && !certutil.options[opt_SourceDir].activated) { PR_fprintf(PR_STDERR, "%s --merge: specify an source database directory " "(--source-dir)\n", progName); return 255; } /* To make a cert, need either a issuer or to self-sign it. */ if (certutil.commands[cmd_CreateAndAddCert].activated && !(certutil.options[opt_IssuerName].activated || certutil.options[opt_SelfSign].activated)) { PR_fprintf(PR_STDERR, "%s -S: must specify issuer (-c) or self-sign (-x).\n", progName); return 255; } /* Using slotname == NULL for listing keys and certs on all slots, * but only that. */ if (!(certutil.commands[cmd_ListKeys].activated || certutil.commands[cmd_DumpChain].activated || certutil.commands[cmd_ListCerts].activated) && slotname == NULL) { PR_fprintf(PR_STDERR, "%s -%c: cannot use \"-h all\" for this command.\n", progName, commandToRun); return 255; } /* Using keytype == nullKey for list all key types, but only that. */ if (!certutil.commands[cmd_ListKeys].activated && keytype == nullKey) { PR_fprintf(PR_STDERR, "%s -%c: cannot use \"-k all\" for this command.\n", progName, commandToRun); return 255; } /* Open the input file. */ if (certutil.options[opt_InputFile].activated) { inFile = PR_Open(certutil.options[opt_InputFile].arg, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for reading (%ld, %ld).\n", progName, certutil.options[opt_InputFile].arg, PR_GetError(), PR_GetOSError()); return 255; } } /* Open the output file. */ if (certutil.options[opt_OutputFile].activated) { outFile = PR_Open(certutil.options[opt_OutputFile].arg, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); if (!outFile) { PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for writing (%ld, %ld).\n", progName, certutil.options[opt_OutputFile].arg, PR_GetError(), PR_GetOSError()); return 255; } } name = SECU_GetOptionArg(&certutil, opt_Nickname); newName = SECU_GetOptionArg(&certutil, opt_NewNickname); email = SECU_GetOptionArg(&certutil, opt_Emailaddress); PK11_SetPasswordFunc(SECU_GetModulePassword); if (PR_TRUE == initialize) { /* Initialize NSPR and NSS. */ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); if (!certutil.commands[cmd_UpgradeMerge].activated) { rv = NSS_Initialize(SECU_ConfigDirectory(NULL), certPrefix, certPrefix, "secmod.db", readOnly ? NSS_INIT_READONLY : 0); } else { rv = NSS_InitWithMerge(SECU_ConfigDirectory(NULL), certPrefix, certPrefix, "secmod.db", sourceDir, srcCertPrefix, srcCertPrefix, upgradeID, upgradeTokenName, readOnly ? NSS_INIT_READONLY : 0); } if (rv != SECSuccess) { SECU_PrintPRandOSError(progName); rv = SECFailure; goto shutdown; } initialized = PR_TRUE; SECU_RegisterDynamicOids(); /* Ensure the SSL error code table has been registered. Bug 1460284. */ SSL_OptionSetDefault(-1, 0); } certHandle = CERT_GetDefaultCertDB(); if (certutil.commands[cmd_Version].activated) { printf("Certificate database content version: command not implemented.\n"); } if (PL_strcmp(slotname, "internal") == 0) slot = PK11_GetInternalKeySlot(); else if (slotname != NULL) slot = PK11_FindSlotByName(slotname); if (!slot && (certutil.commands[cmd_NewDBs].activated || certutil.commands[cmd_ModifyCertTrust].activated || certutil.commands[cmd_ChangePassword].activated || certutil.commands[cmd_TokenReset].activated || certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_AddCert].activated || certutil.commands[cmd_Merge].activated || certutil.commands[cmd_UpgradeMerge].activated || certutil.commands[cmd_AddEmailCert].activated)) { SECU_PrintError(progName, "could not find the slot %s", slotname); rv = SECFailure; goto shutdown; } /* If creating new database, initialize the password. */ if (certutil.commands[cmd_NewDBs].activated) { if (certutil.options[opt_EmptyPassword].activated && (PK11_NeedUserInit(slot))) { rv = PK11_InitPin(slot, (char *)NULL, ""); } else { rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg, certutil.options[opt_NewPasswordFile].arg); } if (rv != SECSuccess) { SECU_PrintError(progName, "Could not set password for the slot"); goto shutdown; } } /* if we are going to modify the cert database, * make sure it's initialized */ if (certutil.commands[cmd_ModifyCertTrust].activated || certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_AddCert].activated || certutil.commands[cmd_AddEmailCert].activated) { if (PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { char *password = NULL; /* fetch the password from the command line or the file * if no password is supplied, initialize the password to NULL */ if (pwdata.source == PW_FROMFILE) { password = SECU_FilePasswd(slot, PR_FALSE, pwdata.data); } else if (pwdata.source == PW_PLAINTEXT) { password = PL_strdup(pwdata.data); } rv = PK11_InitPin(slot, (char *)NULL, password ? password : ""); if (password) { PORT_Memset(password, 0, PL_strlen(password)); PORT_Free(password); } if (rv != SECSuccess) { SECU_PrintError(progName, "Could not set password for the slot"); goto shutdown; } } } /* walk through the upgrade merge if necessary. * This option is more to test what some applications will want to do * to do an automatic upgrade. The --merge command is more useful for * the general case where 2 database need to be merged together. */ if (certutil.commands[cmd_UpgradeMerge].activated) { if (*upgradeTokenName == 0) { upgradeTokenName = upgradeID; } if (!PK11_IsInternal(slot)) { fprintf(stderr, "Only internal DB's can be upgraded\n"); rv = SECSuccess; goto shutdown; } if (!PK11_IsRemovable(slot)) { printf("database already upgraded.\n"); rv = SECSuccess; goto shutdown; } if (!PK11_NeedLogin(slot)) { printf("upgrade complete!\n"); rv = SECSuccess; goto shutdown; } /* authenticate to the old DB if necessary */ if (PORT_Strcmp(PK11_GetTokenName(slot), upgradeTokenName) == 0) { /* if we need a password, supply it. This will be the password * for the old database */ rv = PK11_Authenticate(slot, PR_FALSE, &pwdata2); if (rv != SECSuccess) { SECU_PrintError(progName, "Could not get password for %s", upgradeTokenName); goto shutdown; } /* * if we succeeded above, but still aren't logged in, that means * we just supplied the password for the old database. We may * need the password for the new database. NSS will automatically * change the token names at this point */ if (PK11_IsLoggedIn(slot, &pwdata)) { printf("upgrade complete!\n"); rv = SECSuccess; goto shutdown; } } /* call PK11_IsPresent to update our cached token information */ if (!PK11_IsPresent(slot)) { /* this shouldn't happen. We call isPresent to force a token * info update */ fprintf(stderr, "upgrade/merge internal error\n"); rv = SECFailure; goto shutdown; } /* the token is now set to the state of the source database, * if we need a password for it, PK11_Authenticate will * automatically prompt us */ rv = PK11_Authenticate(slot, PR_FALSE, &pwdata); if (rv == SECSuccess) { printf("upgrade complete!\n"); } else { SECU_PrintError(progName, "Could not get password for %s", PK11_GetTokenName(slot)); } goto shutdown; } /* * merge 2 databases. */ if (certutil.commands[cmd_Merge].activated) { PK11SlotInfo *sourceSlot = NULL; PK11MergeLog *log; char *modspec = PR_smprintf( "configDir='%s' certPrefix='%s' tokenDescription='%s'", sourceDir, srcCertPrefix, *upgradeTokenName ? upgradeTokenName : "Source Database"); if (!modspec) { rv = SECFailure; goto shutdown; } sourceSlot = SECMOD_OpenUserDB(modspec); PR_smprintf_free(modspec); if (!sourceSlot) { SECU_PrintError(progName, "couldn't open source database"); rv = SECFailure; goto shutdown; } rv = PK11_Authenticate(slot, PR_FALSE, &pwdata); if (rv != SECSuccess) { SECU_PrintError(progName, "Couldn't get password for %s", PK11_GetTokenName(slot)); goto merge_fail; } rv = PK11_Authenticate(sourceSlot, PR_FALSE, &pwdata2); if (rv != SECSuccess) { SECU_PrintError(progName, "Couldn't get password for %s", PK11_GetTokenName(sourceSlot)); goto merge_fail; } log = PK11_CreateMergeLog(); if (!log) { rv = SECFailure; SECU_PrintError(progName, "couldn't create error log"); goto merge_fail; } rv = PK11_MergeTokens(slot, sourceSlot, log, &pwdata, &pwdata2); if (rv != SECSuccess) { DumpMergeLog(progName, log); } PK11_DestroyMergeLog(log); merge_fail: SECMOD_CloseUserDB(sourceSlot); PK11_FreeSlot(sourceSlot); goto shutdown; } /* The following 8 options are mutually exclusive with all others. */ /* List certs (-L) */ if (certutil.commands[cmd_ListCerts].activated) { if (certutil.options[opt_DumpExtensionValue].activated) { const char *oid_str; SECItem oid_item; SECStatus srv; oid_item.data = NULL; oid_item.len = 0; oid_str = certutil.options[opt_DumpExtensionValue].arg; srv = GetOidFromString(NULL, &oid_item, oid_str, strlen(oid_str)); if (srv != SECSuccess) { SECU_PrintError(progName, "malformed extension OID %s", oid_str); goto shutdown; } rv = ListCerts(certHandle, name, email, slot, PR_TRUE /*binary*/, PR_FALSE /*ascii*/, &oid_item, outFile, &pwdata); SECITEM_FreeItem(&oid_item, PR_FALSE); } else { rv = ListCerts(certHandle, name, email, slot, certutil.options[opt_BinaryDER].activated, certutil.options[opt_ASCIIForIO].activated, NULL, outFile, &pwdata); } goto shutdown; } if (certutil.commands[cmd_DumpChain].activated) { rv = DumpChain(certHandle, name, certutil.options[opt_ASCIIForIO].activated, certutil.options[opt_SimpleSelfSigned].activated); goto shutdown; } /* XXX needs work */ /* List keys (-K) */ if (certutil.commands[cmd_ListKeys].activated) { rv = ListKeys(slot, name, 0 /*keyindex*/, keytype, PR_FALSE /*dopriv*/, &pwdata); goto shutdown; } /* List modules (-U) */ if (certutil.commands[cmd_ListModules].activated) { rv = ListModules(); goto shutdown; } /* Delete cert (-D) */ if (certutil.commands[cmd_DeleteCert].activated) { rv = DeleteCert(certHandle, name, &pwdata); goto shutdown; } /* Rename cert (--rename) */ if (certutil.commands[cmd_Rename].activated) { rv = RenameCert(certHandle, name, newName, &pwdata); goto shutdown; } /* Delete key (-F) */ if (certutil.commands[cmd_DeleteKey].activated) { if (certutil.options[opt_Nickname].activated) { rv = DeleteCertAndKey(name, &pwdata); } else { privkey = findPrivateKeyByID(slot, keysource, &pwdata); if (!privkey) { SECU_PrintError(progName, "%s is not a key-id", keysource); rv = SECFailure; } else { rv = DeleteKey(privkey, &pwdata); /* already destroyed by PK11_DeleteTokenPrivateKey */ privkey = NULL; } } goto shutdown; } /* Modify trust attribute for cert (-M) */ if (certutil.commands[cmd_ModifyCertTrust].activated) { rv = ChangeTrustAttributes(certHandle, slot, name, certutil.options[opt_Trust].arg, &pwdata); goto shutdown; } /* Change key db password (-W) (future - change pw to slot?) */ if (certutil.commands[cmd_ChangePassword].activated) { rv = SECU_ChangePW2(slot, 0, 0, certutil.options[opt_PasswordFile].arg, certutil.options[opt_NewPasswordFile].arg); if (rv != SECSuccess) { SECU_PrintError(progName, "Could not set password for the slot"); goto shutdown; } } /* Reset the a token */ if (certutil.commands[cmd_TokenReset].activated) { char *sso_pass = ""; if (certutil.options[opt_SSOPass].activated) { sso_pass = certutil.options[opt_SSOPass].arg; } rv = PK11_ResetToken(slot, sso_pass); goto shutdown; } /* Check cert validity against current time (-V) */ if (certutil.commands[cmd_CheckCertValidity].activated) { /* XXX temporary hack for fips - must log in to get priv key */ if (certutil.options[opt_VerifySig].activated) { if (slot && PK11_NeedLogin(slot)) { SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (newrv != SECSuccess) { SECU_PrintError(progName, "could not authenticate to token %s.", PK11_GetTokenName(slot)); goto shutdown; } } } rv = ValidateCert(certHandle, name, certutil.options[opt_ValidityTime].arg, certutil.options[opt_Usage].arg, certutil.options[opt_VerifySig].activated, certutil.options[opt_DetailedInfo].activated, certutil.options[opt_ASCIIForIO].activated, &pwdata); if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INVALID_ARGS) SECU_PrintError(progName, "validation failed"); goto shutdown; } /* * Key generation */ /* These commands may require keygen. */ if (certutil.commands[cmd_CertReq].activated || certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_GenKeyPair].activated) { if (keysource) { CERTCertificate *keycert; keycert = CERT_FindCertByNicknameOrEmailAddr(certHandle, keysource); if (!keycert) { keycert = PK11_FindCertFromNickname(keysource, NULL); } if (keycert) { privkey = PK11_FindKeyByDERCert(slot, keycert, &pwdata); } else { /* Interpret keysource as CKA_ID */ privkey = findPrivateKeyByID(slot, keysource, &pwdata); } if (!privkey) { SECU_PrintError( progName, "%s is neither a key-type nor a nickname nor a key-id", keysource); return SECFailure; } pubkey = SECKEY_ConvertToPublicKey(privkey); if (!pubkey) { SECU_PrintError(progName, "Could not get keys from cert %s", keysource); if (keycert) { CERT_DestroyCertificate(keycert); } rv = SECFailure; goto shutdown; } keytype = privkey->keyType; /* On CertReq for renewal if no subject has been * specified obtain it from the certificate. */ if (certutil.commands[cmd_CertReq].activated && !subject) { if (keycert) { subject = CERT_AsciiToName(keycert->subjectName); if (!subject) { SECU_PrintError( progName, "Could not get subject from certificate %s", keysource); CERT_DestroyCertificate(keycert); rv = SECFailure; goto shutdown; } } else { SECU_PrintError(progName, "Subject name not provided"); rv = SECFailure; goto shutdown; } } if (keycert) { CERT_DestroyCertificate(keycert); } } else { privkey = CERTUTIL_GeneratePrivateKey(keytype, slot, keysize, publicExponent, certutil.options[opt_NoiseFile].arg, &pubkey, certutil.options[opt_PQGFile].arg, keyAttrFlags, keyOpFlagsOn, keyOpFlagsOff, &pwdata); if (privkey == NULL) { SECU_PrintError(progName, "unable to generate key(s)\n"); rv = SECFailure; goto shutdown; } } privkey->wincx = &pwdata; PORT_Assert(pubkey != NULL); /* If all that was needed was keygen, exit. */ if (certutil.commands[cmd_GenKeyPair].activated) { rv = SECSuccess; goto shutdown; } } if (certutil.options[opt_Pss].activated) { if (!certutil.commands[cmd_CertReq].activated && !certutil.commands[cmd_CreateAndAddCert].activated) { PR_fprintf(PR_STDERR, "%s -%c: --pss only works with -R or -S.\n", progName, commandToRun); return 255; } if (keytype != rsaKey) { PR_fprintf(PR_STDERR, "%s -%c: --pss only works with RSA keys.\n", progName, commandToRun); return 255; } } /* --pss-sign is to sign a certificate with RSA-PSS, even if the * issuer's key is an RSA key. If the key is an RSA-PSS key, the * generated signature is always RSA-PSS. */ if (certutil.options[opt_PssSign].activated) { if (!certutil.commands[cmd_CreateNewCert].activated && !certutil.commands[cmd_CreateAndAddCert].activated) { PR_fprintf(PR_STDERR, "%s -%c: --pss-sign only works with -C or -S.\n", progName, commandToRun); return 255; } if (keytype != rsaKey) { PR_fprintf(PR_STDERR, "%s -%c: --pss-sign only works with RSA keys.\n", progName, commandToRun); return 255; } } if (certutil.options[opt_SimpleSelfSigned].activated && !certutil.commands[cmd_DumpChain].activated) { PR_fprintf(PR_STDERR, "%s -%c: --simple-self-signed only works with -O.\n", progName, commandToRun); return 255; } /* If we need a list of extensions convert the flags into list format */ if (certutil.commands[cmd_CertReq].activated || certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_CreateNewCert].activated) { certutil_extns[ext_keyUsage].activated = certutil.options[opt_AddCmdKeyUsageExt].activated; if (!certutil_extns[ext_keyUsage].activated) { certutil_extns[ext_keyUsage].activated = certutil.options[opt_AddKeyUsageExt].activated; } else { certutil_extns[ext_keyUsage].arg = certutil.options[opt_AddCmdKeyUsageExt].arg; } certutil_extns[ext_basicConstraint].activated = certutil.options[opt_AddBasicConstraintExt].activated; certutil_extns[ext_nameConstraints].activated = certutil.options[opt_AddNameConstraintsExt].activated; certutil_extns[ext_authorityKeyID].activated = certutil.options[opt_AddAuthorityKeyIDExt].activated; certutil_extns[ext_subjectKeyID].activated = certutil.options[opt_AddSubjectKeyIDExt].activated; certutil_extns[ext_CRLDistPts].activated = certutil.options[opt_AddCRLDistPtsExt].activated; certutil_extns[ext_NSCertType].activated = certutil.options[opt_AddCmdNSCertTypeExt].activated; if (!certutil_extns[ext_NSCertType].activated) { certutil_extns[ext_NSCertType].activated = certutil.options[opt_AddNSCertTypeExt].activated; } else { certutil_extns[ext_NSCertType].arg = certutil.options[opt_AddCmdNSCertTypeExt].arg; } certutil_extns[ext_extKeyUsage].activated = certutil.options[opt_AddCmdExtKeyUsageExt].activated; if (!certutil_extns[ext_extKeyUsage].activated) { certutil_extns[ext_extKeyUsage].activated = certutil.options[opt_AddExtKeyUsageExt].activated; } else { certutil_extns[ext_extKeyUsage].arg = certutil.options[opt_AddCmdExtKeyUsageExt].arg; } certutil_extns[ext_subjectAltName].activated = certutil.options[opt_AddSubjectAltNameExt].activated; if (certutil_extns[ext_subjectAltName].activated) { certutil_extns[ext_subjectAltName].arg = certutil.options[opt_AddSubjectAltNameExt].arg; } certutil_extns[ext_authInfoAcc].activated = certutil.options[opt_AddAuthInfoAccExt].activated; certutil_extns[ext_subjInfoAcc].activated = certutil.options[opt_AddSubjInfoAccExt].activated; certutil_extns[ext_certPolicies].activated = certutil.options[opt_AddCertPoliciesExt].activated; certutil_extns[ext_policyMappings].activated = certutil.options[opt_AddPolicyMapExt].activated; certutil_extns[ext_policyConstr].activated = certutil.options[opt_AddPolicyConstrExt].activated; certutil_extns[ext_inhibitAnyPolicy].activated = certutil.options[opt_AddInhibAnyExt].activated; } /* -A -C or -E Read inFile */ if (certutil.commands[cmd_CreateNewCert].activated || certutil.commands[cmd_AddCert].activated || certutil.commands[cmd_AddEmailCert].activated) { PRBool isCreate = certutil.commands[cmd_CreateNewCert].activated; rv = SECU_ReadDERFromFile(isCreate ? &certReqDER : &certDER, inFile, certutil.options[opt_ASCIIForIO].activated, PR_TRUE); if (rv) goto shutdown; } /* * Certificate request */ /* Make a cert request (-R). */ if (certutil.commands[cmd_CertReq].activated) { rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, certutil.options[opt_PhoneNumber].arg, certutil.options[opt_ASCIIForIO].activated, certutil.options[opt_ExtendedEmailAddrs].arg, certutil.options[opt_ExtendedDNSNames].arg, certutil_extns, (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg : NULL), certutil.options[opt_Pss].activated, &certReqDER); if (rv) goto shutdown; privkey->wincx = &pwdata; } /* * Certificate creation */ /* If making and adding a cert, create a cert request file first without * any extensions, then load it with the command line extensions * and output the cert to another file. */ if (certutil.commands[cmd_CreateAndAddCert].activated) { static certutilExtnList nullextnlist = { { PR_FALSE, NULL } }; rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, certutil.options[opt_PhoneNumber].arg, PR_FALSE, /* do not BASE64-encode regardless of -a option */ NULL, NULL, nullextnlist, (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg : NULL), certutil.options[opt_Pss].activated, &certReqDER); if (rv) goto shutdown; privkey->wincx = &pwdata; } /* Create a certificate (-C or -S). */ if (certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_CreateNewCert].activated) { rv = CreateCert(certHandle, slot, certutil.options[opt_IssuerName].arg, &certReqDER, &privkey, &pwdata, hashAlgTag, serialNumber, warpmonths, validityMonths, certutil.options[opt_ExtendedEmailAddrs].arg, certutil.options[opt_ExtendedDNSNames].arg, certutil.options[opt_ASCIIForIO].activated && certutil.commands[cmd_CreateNewCert].activated, certutil.options[opt_SelfSign].activated, certutil_extns, (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg : NULL), certVersion, certutil.options[opt_PssSign].activated, &certDER); if (rv) goto shutdown; } /* * Adding a cert to the database (or slot) */ /* -A -E or -S Add the cert to the DB */ if (certutil.commands[cmd_CreateAndAddCert].activated || certutil.commands[cmd_AddCert].activated || certutil.commands[cmd_AddEmailCert].activated) { if (strstr(certutil.options[opt_Trust].arg, "u")) { fprintf(stderr, "Notice: Trust flag u is set automatically if the " "private key is present.\n"); } rv = AddCert(slot, certHandle, name, certutil.options[opt_Trust].arg, &certDER, certutil.commands[cmd_AddEmailCert].activated, &pwdata); if (rv) goto shutdown; } if (certutil.commands[cmd_CertReq].activated || certutil.commands[cmd_CreateNewCert].activated) { SECItem *item = certutil.commands[cmd_CertReq].activated ? &certReqDER : &certDER; PRInt32 written = PR_Write(outFile, item->data, item->len); if (written < 0 || (PRUint32)written != item->len) { rv = SECFailure; } } shutdown: if (slot) { PK11_FreeSlot(slot); } if (privkey) { SECKEY_DestroyPrivateKey(privkey); } if (pubkey) { SECKEY_DestroyPublicKey(pubkey); } if (subject) { CERT_DestroyName(subject); } if (name) { PL_strfree(name); } if (newName) { PL_strfree(newName); } if (inFile && inFile != PR_STDIN) { PR_Close(inFile); } if (outFile && outFile != PR_STDOUT) { PR_Close(outFile); } SECITEM_FreeItem(&certReqDER, PR_FALSE); SECITEM_FreeItem(&certDER, PR_FALSE); if (pwdata.data && pwdata.source == PW_PLAINTEXT) { /* Allocated by a PL_strdup call in SECU_GetModulePassword. */ PL_strfree(pwdata.data); } if (email) { PL_strfree(email); } /* Open the batch command file. * * - If -B option is specified, the contents in the * command file will be interpreted as subsequent certutil * commands to be executed in the current certutil process * context after the current certutil command has been executed. * - Each line in the command file consists of the command * line arguments for certutil. * - The -d option will be ignored if specified in the * command file. * - Quoting with double quote characters ("...") is supported * to allow white space in a command line argument. The * double quote character cannot be escaped and quoting cannot * be nested in this version. * - each line in the batch file is limited to 512 characters */ if ((SECSuccess == rv) && certutil.commands[cmd_Batch].activated) { FILE *batchFile = NULL; char *nextcommand = NULL; PRInt32 cmd_len = 0, buf_size = 0; static const int increment = 512; if (!certutil.options[opt_InputFile].activated || !certutil.options[opt_InputFile].arg) { PR_fprintf(PR_STDERR, "%s: no batch input file specified.\n", progName); return 255; } batchFile = fopen(certutil.options[opt_InputFile].arg, "r"); if (!batchFile) { PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for reading (%ld, %ld).\n", progName, certutil.options[opt_InputFile].arg, PR_GetError(), PR_GetOSError()); return 255; } /* read and execute command-lines in a loop */ while (SECSuccess == rv) { PRBool invalid = PR_FALSE; int newargc = 2; char *space = NULL; char *nextarg = NULL; char **newargv = NULL; char *crlf; if (cmd_len + increment > buf_size) { char *new_buf; buf_size += increment; new_buf = PORT_Realloc(nextcommand, buf_size); if (!new_buf) { PR_fprintf(PR_STDERR, "%s: PORT_Realloc(%ld) failed\n", progName, buf_size); break; } nextcommand = new_buf; nextcommand[cmd_len] = '\0'; } if (!fgets(nextcommand + cmd_len, buf_size - cmd_len, batchFile)) { break; } crlf = PORT_Strrchr(nextcommand, '\n'); if (crlf) { *crlf = '\0'; } cmd_len = strlen(nextcommand); if (cmd_len && nextcommand[cmd_len - 1] == '\\') { nextcommand[--cmd_len] = '\0'; continue; } /* we now need to split the command into argc / argv format */ newargv = PORT_Alloc(sizeof(char *) * (newargc + 1)); newargv[0] = progName; newargv[1] = nextcommand; nextarg = nextcommand; while ((space = PORT_Strpbrk(nextarg, " \f\n\r\t\v"))) { while (isspace(*space)) { *space = '\0'; space++; } if (*space == '\0') { break; } else if (*space != '\"') { nextarg = space; } else { char *closingquote = strchr(space + 1, '\"'); if (closingquote) { *closingquote = '\0'; space++; nextarg = closingquote + 1; } else { invalid = PR_TRUE; nextarg = space; } } newargc++; newargv = PORT_Realloc(newargv, sizeof(char *) * (newargc + 1)); newargv[newargc - 1] = space; } newargv[newargc] = NULL; /* invoke next command */ if (PR_TRUE == invalid) { PR_fprintf(PR_STDERR, "Missing closing quote in batch command :\n%s\nNot executed.\n", nextcommand); rv = SECFailure; } else { if (0 != certutil_main(newargc, newargv, PR_FALSE)) rv = SECFailure; } PORT_Free(newargv); cmd_len = 0; nextcommand[0] = '\0'; } PORT_Free(nextcommand); fclose(batchFile); } if ((initialized == PR_TRUE) && NSS_Shutdown() != SECSuccess) { exit(1); } if (rv == SECSuccess) { return 0; } else { return 255; } } int main(int argc, char **argv) { int rv = certutil_main(argc, argv, PR_TRUE); PL_ArenaFinish(); PR_Cleanup(); return rv; }