1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nss_scoped_ptrs.h"
22 const std::vector
<std::string
> kCommandArgs(
23 {"--create", "--list-certs", "--import-cert", "--list-keys", "--import-key",
24 "--delete-cert", "--delete-key", "--change-password"});
26 static bool HasSingleCommandArgument(const ArgParser
&parser
) {
27 auto pred
= [&](const std::string
&cmd
) { return parser
.Has(cmd
); };
28 return std::count_if(kCommandArgs
.begin(), kCommandArgs
.end(), pred
) == 1;
31 static bool HasArgumentRequiringWriteAccess(const ArgParser
&parser
) {
32 return parser
.Has("--create") || parser
.Has("--import-cert") ||
33 parser
.Has("--import-key") || parser
.Has("--delete-cert") ||
34 parser
.Has("--delete-key") || parser
.Has("--change-password");
37 static std::string
PrintFlags(unsigned int flags
) {
39 if ((flags
& CERTDB_VALID_CA
) && !(flags
& CERTDB_TRUSTED_CA
) &&
40 !(flags
& CERTDB_TRUSTED_CLIENT_CA
)) {
43 if ((flags
& CERTDB_TERMINAL_RECORD
) && !(flags
& CERTDB_TRUSTED
)) {
46 if (flags
& CERTDB_TRUSTED_CA
) {
49 if (flags
& CERTDB_TRUSTED_CLIENT_CA
) {
52 if (flags
& CERTDB_TRUSTED
) {
55 if (flags
& CERTDB_USER
) {
58 if (flags
& CERTDB_SEND_WARN
) {
61 if (flags
& CERTDB_INVISIBLE_CA
) {
64 if (flags
& CERTDB_GOVT_APPROVED_CA
) {
70 static const char *const keyTypeName
[] = {"null", "rsa", "dsa", "fortezza",
73 void DBTool::Usage() {
74 std::cerr
<< "Usage: nss db [--path <directory>]" << std::endl
;
75 std::cerr
<< " --create" << std::endl
;
76 std::cerr
<< " --change-password" << std::endl
;
77 std::cerr
<< " --list-certs" << std::endl
;
78 std::cerr
<< " --import-cert [<path>] --name <name> [--trusts <trusts>]"
80 std::cerr
<< " --list-keys" << std::endl
;
81 std::cerr
<< " --import-key [<path> [-- name <name>]]" << std::endl
;
82 std::cerr
<< " --delete-cert <name>" << std::endl
;
83 std::cerr
<< " --delete-key <name>" << std::endl
;
86 bool DBTool::Run(const std::vector
<std::string
> &arguments
) {
87 ArgParser
parser(arguments
);
89 if (!HasSingleCommandArgument(parser
)) {
94 PRAccessHow how
= PR_ACCESS_READ_OK
;
96 if (HasArgumentRequiringWriteAccess(parser
)) {
97 how
= PR_ACCESS_WRITE_OK
;
101 std::string
initDir(".");
102 if (parser
.Has("--path")) {
103 initDir
= parser
.Get("--path");
105 if (PR_Access(initDir
.c_str(), how
) != PR_SUCCESS
) {
106 std::cerr
<< "Directory '" << initDir
107 << "' does not exist or you don't have permissions!" << std::endl
;
111 std::cout
<< "Using database directory: " << initDir
<< std::endl
114 bool dbFilesExist
= PathHasDBFiles(initDir
);
115 if (parser
.Has("--create") && dbFilesExist
) {
116 std::cerr
<< "Trying to create database files in a directory where they "
117 "already exists. Delete the db files before creating new ones."
121 if (!parser
.Has("--create") && !dbFilesExist
) {
122 std::cerr
<< "No db files found." << std::endl
;
123 std::cerr
<< "Create them using 'nss db --create [--path /foo/bar]' before "
130 const char *certPrefix
= ""; // certutil -P option --- can leave this empty
131 SECStatus rv
= NSS_Initialize(initDir
.c_str(), certPrefix
, certPrefix
,
132 "secmod.db", readOnly
? NSS_INIT_READONLY
: 0);
133 if (rv
!= SECSuccess
) {
134 std::cerr
<< "NSS init failed!" << std::endl
;
139 if (parser
.Has("--list-certs")) {
141 } else if (parser
.Has("--import-cert")) {
142 ret
= ImportCertificate(parser
);
143 } else if (parser
.Has("--create")) {
144 ret
= InitSlotPassword();
146 std::cout
<< "DB files created successfully." << std::endl
;
148 } else if (parser
.Has("--list-keys")) {
150 } else if (parser
.Has("--import-key")) {
151 ret
= ImportKey(parser
);
152 } else if (parser
.Has("--delete-cert")) {
153 ret
= DeleteCert(parser
);
154 } else if (parser
.Has("--delete-key")) {
155 ret
= DeleteKey(parser
);
156 } else if (parser
.Has("--change-password")) {
157 ret
= ChangeSlotPassword();
161 if (NSS_Shutdown() != SECSuccess
) {
162 std::cerr
<< "NSS Shutdown failed!" << std::endl
;
169 bool DBTool::PathHasDBFiles(std::string path
) {
170 std::regex
certDBPattern("cert.*\\.db");
171 std::regex
keyDBPattern("key.*\\.db");
173 PRDir
*dir
= PR_OpenDir(path
.c_str());
175 std::cerr
<< "Directory " << path
<< " could not be accessed!" << std::endl
;
180 bool dbFileExists
= false;
181 while ((ent
= PR_ReadDir(dir
, PR_SKIP_BOTH
))) {
182 if (std::regex_match(ent
->name
, certDBPattern
) ||
183 std::regex_match(ent
->name
, keyDBPattern
) ||
184 "secmod.db" == std::string(ent
->name
)) {
190 (void)PR_CloseDir(dir
);
194 void DBTool::ListCertificates() {
195 ScopedCERTCertList
list(PK11_ListCerts(PK11CertListAll
, nullptr));
196 CERTCertListNode
*node
;
198 std::cout
<< std::setw(60) << std::left
<< "Certificate Nickname"
200 << "Trust Attributes" << std::endl
;
201 std::cout
<< std::setw(60) << std::left
<< ""
203 << "SSL,S/MIME,JAR/XPI" << std::endl
206 for (node
= CERT_LIST_HEAD(list
); !CERT_LIST_END(node
, list
);
207 node
= CERT_LIST_NEXT(node
)) {
208 CERTCertificate
*cert
= node
->cert
;
210 std::string
name("(unknown)");
211 char *appData
= static_cast<char *>(node
->appData
);
212 if (appData
&& strlen(appData
) > 0) {
214 } else if (cert
->nickname
&& strlen(cert
->nickname
) > 0) {
215 name
= cert
->nickname
;
216 } else if (cert
->emailAddr
&& strlen(cert
->emailAddr
) > 0) {
217 name
= cert
->emailAddr
;
222 if (CERT_GetCertTrust(cert
, &trust
) == SECSuccess
) {
223 std::stringstream ss
;
224 ss
<< PrintFlags(trust
.sslFlags
);
226 ss
<< PrintFlags(trust
.emailFlags
);
228 ss
<< PrintFlags(trust
.objectSigningFlags
);
233 std::cout
<< std::setw(60) << std::left
<< name
<< " " << trusts
238 bool DBTool::ImportCertificate(const ArgParser
&parser
) {
239 if (!parser
.Has("--name")) {
240 std::cerr
<< "A name (--name) is required to import a certificate."
246 std::string derFilePath
= parser
.Get("--import-cert");
247 std::string certName
= parser
.Get("--name");
248 std::string
trustString("TCu,Cu,Tu");
249 if (parser
.Has("--trusts")) {
250 trustString
= parser
.Get("--trusts");
254 SECStatus rv
= CERT_DecodeTrustString(&trust
, trustString
.c_str());
255 if (rv
!= SECSuccess
) {
256 std::cerr
<< "Cannot decode trust string!" << std::endl
;
260 ScopedPK11SlotInfo
slot(PK11_GetInternalKeySlot());
261 if (slot
.get() == nullptr) {
262 std::cerr
<< "Error: Init PK11SlotInfo failed!" << std::endl
;
266 std::vector
<uint8_t> certData
= ReadInputData(derFilePath
);
268 ScopedCERTCertificate
cert(CERT_DecodeCertFromPackage(
269 reinterpret_cast<char *>(certData
.data()), certData
.size()));
270 if (cert
.get() == nullptr) {
271 std::cerr
<< "Error: Could not decode certificate!" << std::endl
;
275 rv
= PK11_ImportCert(slot
.get(), cert
.get(), CK_INVALID_HANDLE
,
276 certName
.c_str(), PR_FALSE
);
277 if (rv
!= SECSuccess
) {
278 // TODO handle authentication -> PK11_Authenticate (see certutil.c line
280 std::cerr
<< "Error: Could not add certificate to database!" << std::endl
;
284 rv
= CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert
.get(), &trust
);
285 if (rv
!= SECSuccess
) {
286 std::cerr
<< "Cannot change cert's trust" << std::endl
;
290 std::cout
<< "Certificate import was successful!" << std::endl
;
291 // TODO show information about imported certificate
295 bool DBTool::ListKeys() {
296 ScopedPK11SlotInfo
slot(PK11_GetInternalKeySlot());
297 if (slot
.get() == nullptr) {
298 std::cerr
<< "Error: Init PK11SlotInfo failed!" << std::endl
;
302 if (!DBLoginIfNeeded(slot
)) {
306 ScopedSECKEYPrivateKeyList
list(PK11_ListPrivateKeysInSlot(slot
.get()));
307 if (list
.get() == nullptr) {
308 std::cerr
<< "Listing private keys failed with error "
309 << PR_ErrorToName(PR_GetError()) << std::endl
;
313 SECKEYPrivateKeyListNode
*node
;
315 for (node
= PRIVKEY_LIST_HEAD(list
.get());
316 !PRIVKEY_LIST_END(node
, list
.get()); node
= PRIVKEY_LIST_NEXT(node
)) {
317 char *keyNameRaw
= PK11_GetPrivateKeyNickname(node
->key
);
318 std::string
keyName(keyNameRaw
? keyNameRaw
: "");
320 if (keyName
.empty()) {
321 ScopedCERTCertificate
cert(PK11_GetCertFromPrivateKey(node
->key
));
323 if (cert
->nickname
&& strlen(cert
->nickname
) > 0) {
324 keyName
= cert
->nickname
;
325 } else if (cert
->emailAddr
&& strlen(cert
->emailAddr
) > 0) {
326 keyName
= cert
->emailAddr
;
329 if (keyName
.empty()) {
330 keyName
= "(none)"; // default value
334 SECKEYPrivateKey
*key
= node
->key
;
335 ScopedSECItem
keyIDItem(PK11_GetLowLevelKeyIDForPrivateKey(key
));
336 if (keyIDItem
.get() == nullptr) {
337 std::cerr
<< "Error: PK11_GetLowLevelKeyIDForPrivateKey failed!"
342 std::string keyID
= StringToHex(keyIDItem
);
346 std::cout
<< std::left
<< std::setw(20) << "<key#, key name>"
347 << std::setw(20) << "key type"
348 << "key id" << std::endl
;
351 std::stringstream leftElem
;
352 leftElem
<< "<" << count
<< ", " << keyName
<< ">";
353 std::cout
<< std::left
<< std::setw(20) << leftElem
.str() << std::setw(20)
354 << keyTypeName
[key
->keyType
] << keyID
<< std::endl
;
358 std::cout
<< "No keys found." << std::endl
;
364 bool DBTool::ImportKey(const ArgParser
&parser
) {
365 std::string privKeyFilePath
= parser
.Get("--import-key");
367 if (parser
.Has("--name")) {
368 name
= parser
.Get("--name");
371 ScopedPK11SlotInfo
slot(PK11_GetInternalKeySlot());
372 if (slot
.get() == nullptr) {
373 std::cerr
<< "Error: Init PK11SlotInfo failed!" << std::endl
;
377 if (!DBLoginIfNeeded(slot
)) {
381 std::vector
<uint8_t> privKeyData
= ReadInputData(privKeyFilePath
);
382 if (privKeyData
.empty()) {
385 SECItem pkcs8PrivKeyItem
= {
386 siBuffer
, reinterpret_cast<unsigned char *>(privKeyData
.data()),
387 static_cast<unsigned int>(privKeyData
.size())};
389 SECItem nickname
= {siBuffer
, nullptr, 0};
391 nickname
.data
= const_cast<unsigned char *>(
392 reinterpret_cast<const unsigned char *>(name
.c_str()));
393 nickname
.len
= static_cast<unsigned int>(name
.size());
396 SECStatus rv
= PK11_ImportDERPrivateKeyInfo(
397 slot
.get(), &pkcs8PrivKeyItem
,
398 nickname
.data
== nullptr ? nullptr : &nickname
, nullptr /*publicValue*/,
399 true /*isPerm*/, false /*isPrivate*/, KU_ALL
, nullptr);
400 if (rv
!= SECSuccess
) {
401 std::cerr
<< "Importing a private key in DER format failed with error "
402 << PR_ErrorToName(PR_GetError()) << std::endl
;
406 std::cout
<< "Key import succeeded." << std::endl
;
410 bool DBTool::DeleteCert(const ArgParser
&parser
) {
411 std::string certName
= parser
.Get("--delete-cert");
412 if (certName
.empty()) {
413 std::cerr
<< "A name is required to delete a certificate." << std::endl
;
418 ScopedCERTCertificate
cert(CERT_FindCertByNicknameOrEmailAddr(
419 CERT_GetDefaultCertDB(), certName
.c_str()));
421 std::cerr
<< "Could not find certificate with name " << certName
<< "."
426 SECStatus rv
= SEC_DeletePermCertificate(cert
.get());
427 if (rv
!= SECSuccess
) {
428 std::cerr
<< "Unable to delete certificate with name " << certName
<< "."
433 std::cout
<< "Certificate with name " << certName
<< " deleted successfully."
438 bool DBTool::DeleteKey(const ArgParser
&parser
) {
439 std::string keyName
= parser
.Get("--delete-key");
440 if (keyName
.empty()) {
441 std::cerr
<< "A name is required to delete a key." << std::endl
;
446 ScopedPK11SlotInfo
slot(PK11_GetInternalKeySlot());
447 if (slot
.get() == nullptr) {
448 std::cerr
<< "Error: Init PK11SlotInfo failed!" << std::endl
;
452 if (!DBLoginIfNeeded(slot
)) {
456 ScopedSECKEYPrivateKeyList
list(PK11_ListPrivKeysInSlot(
457 slot
.get(), const_cast<char *>(keyName
.c_str()), nullptr));
458 if (list
.get() == nullptr) {
459 std::cerr
<< "Fetching private keys with nickname " << keyName
460 << " failed with error " << PR_ErrorToName(PR_GetError())
465 unsigned int foundKeys
= 0, deletedKeys
= 0;
466 SECKEYPrivateKeyListNode
*node
;
467 for (node
= PRIVKEY_LIST_HEAD(list
.get());
468 !PRIVKEY_LIST_END(node
, list
.get()); node
= PRIVKEY_LIST_NEXT(node
)) {
469 SECKEYPrivateKey
*privKey
= node
->key
;
471 // see PK11_DeleteTokenPrivateKey for example usage
472 // calling PK11_DeleteTokenPrivateKey directly does not work because it also
473 // destroys the SECKEYPrivateKey (by calling SECKEY_DestroyPrivateKey) -
474 // then SECKEY_DestroyPrivateKeyList does not
475 // work because it also calls SECKEY_DestroyPrivateKey
477 PK11_DestroyTokenObject(privKey
->pkcs11Slot
, privKey
->pkcs11ID
);
478 if (rv
== SECSuccess
) {
483 if (foundKeys
> deletedKeys
) {
484 std::cerr
<< "Some keys could not be deleted." << std::endl
;
487 if (deletedKeys
> 0) {
488 std::cout
<< "Found " << foundKeys
<< " keys." << std::endl
;
489 std::cout
<< "Successfully deleted " << deletedKeys
490 << " key(s) with nickname " << keyName
<< "." << std::endl
;
492 std::cout
<< "No key with nickname " << keyName
<< " found to delete."