| /* 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 <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #if defined(WIN32) |
| #include "fcntl.h" |
| #include "io.h" |
| #endif |
| |
| #include "secutil.h" |
| |
| #if defined(XP_UNIX) |
| #include <unistd.h> |
| #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 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 = 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 (rv != SECSuccess) { |
| SECU_PrintError(progName, |
| "could not authenticate to token %s.", |
| PK11_GetTokenName(slot)); |
| GEN_BREAK(SECFailure); |
| } |
| rv = CERT_ChangeCertTrust(handle, cert, trust); |
| } |
| 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 = 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 (rv != SECSuccess) { |
| SECU_PrintError(progName, "could not authenticate to token %s.", |
| PK11_GetTokenName(slot)); |
| return SECFailure; |
| } |
| rv = CERT_ChangeCertTrust(handle, cert, trust); |
| } |
| 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) { |
| sprintf(dst, "%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; |
| |
| pwarg = NULL; |
| |
| 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\", \"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 <command> -d <database-directory> <options>\n\n" |
| "Valid commands:\n", |
| progName, progName); |
| LongUsage(usage_selected, NULL); |
| PR_fprintf(PR_STDERR, "\n" |
| "%s -H <command> : 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]; |
| } |
| sprintf(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", |
| commandToRun, progName); |
| 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 <command line> 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 <configdir> 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; |
| } |