summaryrefslogtreecommitdiffstats
path: root/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst')
-rw-r--r--security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst713
1 files changed, 713 insertions, 0 deletions
diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst
new file mode 100644
index 0000000000..10926903af
--- /dev/null
+++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst
@@ -0,0 +1,713 @@
+.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample1:
+
+NSS Sample Code Sample1
+=======================
+
+.. _nss_sample_code_1_key_generation_and_transport_between_servers.:
+
+`NSS Sample Code 1: Key Generation and Transport Between Servers. <#nss_sample_code_1_key_generation_and_transport_between_servers.>`__
+---------------------------------------------------------------------------------------------------------------------------------------
+
+.. container::
+
+ This is an example program that demonstrates how to do key generation and transport between
+ cooperating servers. This program shows the following:
+
+ - RSA key pair generation
+ - Naming RSA key pairs
+ - Looking up a previously generated key pair by name
+ - Creating AES and MAC keys (or encryption and MAC keys in general)
+ - Wrapping symmetric keys using your own RSA key pair so that they can be stored on disk or in a
+ database.
+
+ - As an alternative to TOKEN symmetric keys
+
+ - As a way to store large numbers of symmetric keys
+
+ - Wrapping symmetric keys using an RSA key from another server
+ - Unwrapping keys using your own RSA key pair
+
+ | The main part of the program shows a typical sequence of events for two servers that are trying
+ to extablish a shared key pair.
+ | We will add message protection (encryption and MACing) examples to this program in the future.
+
+.. _sample_code:
+
+`Sample Code <#sample_code>`__
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. container::
+
+ .. code::
+
+ #include <iostream.h>
+ #include "pk11pub.h"
+ #include "keyhi.h"
+ #include "nss.h"
+
+ // Key management for keys share among multiple hosts
+ //
+ // This example shows how to use NSS functions to create and
+ // distribute keys that need to be shared among multiple servers
+ // or hosts.
+ //
+ // The management scheme assumes that one host is PRIMARY. It
+ // generates the secret keys that will be used by all participating
+ // hosts. The other hosts (SECONDARY) request keys from the
+ // primary host. As an alternative, new keys may be sent to the
+ // current set of SECONDARY hosts when they are generated by the
+ // PRIMARY. In this case, the PRIMARY maintains a list of the
+ // secondary hosts.
+ //
+ // The sequence of events is:
+ // 1. The primary host generates a new symmetric key. This key
+ // may be used for an encryption mechanism (DES or AES) or for
+ // integrity (MD5_HMAC or SHA1_HMAC). This key needs to be
+ // permanent, since it may be used during several runs of the
+ // server. (Currently NSS doesn't store persistant keys. Steps
+ // 1a through 1x show how to do this).
+ // 1a. The primary host generates an RSA keypair that will be used
+ // store keys locally.
+ // 1b. The primary host wraps the newly generated key using the
+ // RSA key and stores the wrapped key data in a local file.
+ // 1c. The primary host unwraps the key using the RSA key each time
+ // access to the key is required, such as at server startup.
+ // 2. The secondary host generates an RSA keypair that will be used
+ // to transport keys between the primary host and itself. This
+ // key needs to exist long enough to be used to process the
+ // response to a key transport request that is made to the primary
+ // server. The example here shows how to create a permanent (token)
+ // RSA key for this purpose. (This key will also be used for
+ // storage of the keys, since NSS does not support permanent symmetric
+ // keys at the current time.)
+ // 3. The secondary host sends its RSA public key to the primary host as
+ // part of a request for a particular key, or to be added to a list
+ // of secondary hosts.
+ // 4. The administrator of the primary host verifies that the RSA key
+ // that was received belongs to a valid secondary host. The adminstrator
+ // may do this by checking that the key was received in a signed email
+ // message, or by checking a digest value with the adminstrator of the
+ // secondary host. [Need support for digest check values]
+ // 5. The primary host exports (wraps) the symmetric key using the
+ // secondary host's RSA key. The wrapped value is sent back to
+ // the secondary host.
+ // 6. The administrator of the secondary host verifies that the wrapped
+ // key data came from the primary host. The same methods outlined
+ // in step 4 may be used here.
+ // 7. The secondary host unwraps the key using its own RSA private key.
+ // NOTE: currently NSS does not support permanent symmetric keys.
+ // The secondary host may store the wrapped value that was received
+ // from the primary in a file, and unwrap it each time the key is required
+ // (such as at server startup).
+
+ // NSS actually has some support for permanent symmetric keys. However this
+ // example will need to be modified somewhat in order to demonstrate it.
+
+ // Utility function to print hex data
+ static void
+ printBuffer(unsigned char *digest, unsigned int len)
+ {
+ int i;
+
+ cout << "length: " << len << endl;
+ for(i = 0;i < len;i++) printf("%02x ", digest[i]);
+ cout << endl;
+ }
+
+ // XXX Data protection
+ // - takes an input buffer, applies the encryption
+ // and MAC, and generates a buffer with the result.
+ // - the application sends or uses the result (possibly
+ // after base64 encoding it.
+
+ //
+ // Server - an instance of a server that is part of a
+ // cluster of servers that are sharing a common set
+ // of encryption and MACing keys.
+ //
+ class Server
+ {
+ public:
+ // Initializes the server instance. In particular, this
+ // creates the key pair that is used for wrapping keys
+ int Init();
+
+ // Generates keys for encryption (AES) and MACing. The
+ // wrapped keys are stored in data files.
+ int GenerateKeys();
+
+ // Gets the server's public key (wrapping key) to
+ // send to another server. This becomes the input to
+ // the ExportKeys method on the remote server.
+ int ExportPublicKey(SECItem **pubKeyData);
+
+ // Export the encryption and key using the key
+ // provided. The key should come from another server
+ // in the cluster. (The admin should verify this.)
+ //
+ // In this example, the server must be started to perform
+ // this function (see Start())
+ int ExportKeys(SECItem *pubKey, SECItem **wrappedEncKey,
+ SECItem **wrappedMacKey);
+
+ // Import the keys received from another server in the
+ // cluster. The admin should make sure the keys actually
+ // came from the correct source.
+ int ImportKeys(SECItem *wrappedEncKey, SECItem *wrappedMacKey);
+
+ // Start the server, loading the encryption and MACing keys
+ // from files
+ int Start();
+
+ // Shut down the server. (For completeness)
+ int Shutdown();
+
+ // Compare keys in two server instances. Use this in the
+ // example to make sure the keys are transferred correctly.
+ // This will not work in real life!
+ //
+ // The servers must be started
+ int CompareKeys(Server *peer);
+
+ // Create a server - the name distiguish the keys in the
+ // shared database in this example
+ Server(const char *serverName);
+ ~Server();
+
+ private:
+ int getPrivateKey(SECKEYPrivateKey **prvKey);
+ int getPublicKey(SECKEYPublicKey **pubKey);
+ int wrapKey(PK11SymKey *key, SECKEYPublicKey *pubKey, SECItem **data);
+
+ // export raw key (unwrapped) DO NOT USE
+ int rawExportKey(PK11SymKey *key, SECItem **data);
+
+ char *mServerName;
+
+ // These items represent data that might be stored
+ // in files or in a configuration file
+ SECItem *mWrappedEncKey;
+ SECItem *mWrappedMacKey;
+
+ // These are the runtime keys as loaded from the files
+ PK11SymKey *mEncKey;
+ PK11SymKey *mMacKey;
+ };
+
+ Server::Server(const char *serverName)
+ : mServerName(0), mWrappedEncKey(0), mWrappedMacKey(0),
+ mEncKey(0), mMacKey(0)
+ {
+ // Copy the server name
+ mServerName = PL_strdup(serverName);
+ }
+
+ Server::~Server()
+ {
+ if (mServerName) PL_strfree(mServerName);
+ if (mWrappedEncKey) SECITEM_FreeItem(mWrappedEncKey, PR_TRUE);
+ if (mWrappedMacKey) SECITEM_FreeItem(mWrappedMacKey, PR_TRUE);
+ if (mEncKey) PK11_FreeSymKey(mEncKey);
+ if (mMacKey) PK11_FreeSymKey(mMacKey);
+ }
+
+ int
+ Server::Init()
+ {
+ int rv = 0;
+ SECKEYPrivateKey *prvKey = 0;
+ SECKEYPublicKey *pubKey = 0;
+ PK11SlotInfo *slot = 0;
+ PK11RSAGenParams rsaParams;
+ SECStatus s;
+
+ // See if there is already a private key with this name.
+ // If there is one, no further action is required.
+ rv = getPrivateKey(&prvKey);
+ if (rv == 0 && prvKey) goto done;
+
+ rv = 0;
+
+ // These could be parameters to the Init function
+ rsaParams.keySizeInBits = 1024;
+ rsaParams.pe = 65537;
+
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) { rv = 1; goto done; }
+
+ prvKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams,
+ &pubKey, PR_TRUE, PR_TRUE, 0);
+ if (!prvKey) { rv = 1; goto done; }
+
+ // Set the nickname on the private key so that it
+ // can be found later.
+ s = PK11_SetPrivateKeyNickname(prvKey, mServerName);
+ if (s != SECSuccess) { rv = 1; goto done; }
+
+ done:
+ if (slot) PK11_FreeSlot(slot);
+ if (pubKey) SECKEY_DestroyPublicKey(pubKey);
+ if (prvKey) SECKEY_DestroyPrivateKey(prvKey);
+
+ return rv;
+ }
+
+ int
+ Server::GenerateKeys()
+ {
+ int rv = 0;
+ SECKEYPublicKey *pubKey = 0;
+ PK11SlotInfo *slot = 0;
+
+ // Choose a slot to use
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) { rv = 1; goto done; }
+
+ // Get our own public key to use for wrapping
+ rv = getPublicKey(&pubKey);
+ if (rv) goto done;
+
+ // Do the Encryption (AES) key
+ if (!mWrappedEncKey)
+ {
+ PK11SymKey *key = 0;
+
+ // The key size is 128 bits (16 bytes)
+ key = PK11_KeyGen(slot, CKM_AES_KEY_GEN, 0, 128/8, 0);
+ if (!key) { rv = 1; goto aes_done; }
+
+ rv = wrapKey(key, pubKey, &mWrappedEncKey);
+
+ aes_done:
+ if (key) PK11_FreeSymKey(key);
+
+ if (rv) goto done;
+ }
+
+ // Do the Mac key
+ if (!mWrappedMacKey)
+ {
+ PK11SymKey *key = 0;
+
+ // The key size is 160 bits (20 bytes)
+ key = PK11_KeyGen(slot, CKM_GENERIC_SECRET_KEY_GEN, 0, 160/8, 0);
+ if (!key) { rv = 1; goto mac_done; }
+
+ rv = wrapKey(key, pubKey, &mWrappedMacKey);
+
+ mac_done:
+ if (key) PK11_FreeSymKey(key);
+ }
+
+ done:
+ if (slot) PK11_FreeSlot(slot);
+
+ return rv;
+ }
+
+ int
+ Server::ExportPublicKey(SECItem **pubKeyData)
+ {
+ int rv = 0;
+ SECKEYPublicKey *pubKey = 0;
+
+ rv = getPublicKey(&pubKey);
+ if (rv) goto done;
+
+ *pubKeyData = SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey);
+ if (!*pubKeyData) { rv = 1; goto done; }
+
+ done:
+ if (pubKey) SECKEY_DestroyPublicKey(pubKey);
+
+ return rv;
+ }
+
+ int
+ Server::ExportKeys(SECItem *pubKeyData, SECItem **wrappedEncKey,
+ SECItem **wrappedMacKey)
+ {
+ int rv;
+ CERTSubjectPublicKeyInfo *keyInfo = 0;
+ SECKEYPublicKey *pubKey = 0;
+ SECItem *data = 0;
+
+ // Make sure the keys are available (server running)
+ if (!mEncKey || !mMacKey) { rv = 1; goto done; }
+
+ // Import the public key of the other server
+ keyInfo = SECKEY_DecodeDERSubjectPublicKeyInfo(pubKeyData);
+ if (!keyInfo) { rv = 1; goto done; }
+
+ pubKey = SECKEY_ExtractPublicKey(keyInfo);
+ if (!pubKey) { rv = 1; goto done; }
+
+ // Export the encryption key
+ rv = wrapKey(mEncKey, pubKey, &data);
+ if (rv) goto done;
+
+ // Export the MAC key
+ rv = wrapKey(mMacKey, pubKey, wrappedMacKey);
+ if (rv) goto done;
+
+ // Commit the rest of the operation
+ *wrappedEncKey = data;
+ data = 0;
+
+ done:
+ if (data) SECITEM_FreeItem(data, PR_TRUE);
+ if (pubKey) SECKEY_DestroyPublicKey(pubKey);
+ if (keyInfo) SECKEY_DestroySubjectPublicKeyInfo(keyInfo);
+
+ return rv;
+ }
+
+ int
+ Server::ImportKeys(SECItem *wrappedEncKey, SECItem *wrappedMacKey)
+ {
+ int rv = 0;
+
+ if (mWrappedEncKey || mWrappedMacKey) { rv = 1; goto done; }
+
+ mWrappedEncKey = SECITEM_DupItem(wrappedEncKey);
+ if (!mWrappedEncKey) { rv = 1; goto done; }
+
+ mWrappedMacKey = SECITEM_DupItem(wrappedMacKey);
+ if (!mWrappedMacKey) { rv = 1; goto done; }
+
+ done:
+ return rv;
+ }
+
+ int
+ Server::Start()
+ {
+ int rv;
+ SECKEYPrivateKey *prvKey = 0;
+
+ rv = getPrivateKey(&prvKey);
+ if (rv) goto done;
+
+ if (!mEncKey)
+ {
+ // Unwrap the encryption key from the "file"
+ // This function uses a mechanism rather than a key type
+ // Does this need to be "WithFlags"??
+ mEncKey = PK11_PubUnwrapSymKey(prvKey, mWrappedEncKey,
+ CKM_AES_CBC_PAD, CKA_ENCRYPT, 0);
+ if (!mEncKey) { rv = 1; goto done; }
+ }
+
+ if (!mMacKey)
+ {
+ // Unwrap the MAC key from the "file"
+ // This function uses a mechanism rather than a key type
+ // Does this need to be "WithFlags"??
+ mMacKey = PK11_PubUnwrapSymKey(prvKey, mWrappedMacKey,
+ CKM_MD5_HMAC, CKA_SIGN, 0);
+ if (!mMacKey) { rv = 1; goto done; }
+ }
+
+ done:
+ if (prvKey) SECKEY_DestroyPrivateKey(prvKey);
+
+ return rv;
+ }
+
+ int
+ Server::Shutdown()
+ {
+ if (mEncKey) PK11_FreeSymKey(mEncKey);
+ if (mMacKey) PK11_FreeSymKey(mMacKey);
+
+ mEncKey = 0;
+ mMacKey = 0;
+
+ return 0;
+ }
+
+ int
+ Server::CompareKeys(Server *peer)
+ {
+ int rv;
+ SECItem *macKey1 = 0;
+ SECItem *macKey2 = 0;
+ SECItem *encKey1 = 0;
+ SECItem *encKey2 = 0;
+
+ // Export each of the keys in raw form
+ rv = rawExportKey(mMacKey, &macKey1);
+ if (rv) goto done;
+
+ rv = rawExportKey(peer->mMacKey, &macKey2);
+ if (rv) goto done;
+
+ rv = rawExportKey(mEncKey, &encKey1);
+ if (rv) goto done;
+
+ rv = rawExportKey(peer->mEncKey, &encKey2);
+ if (rv) goto done;
+
+ if (!SECITEM_ItemsAreEqual(macKey1, macKey2)) { rv = 1; goto done; }
+ if (!SECITEM_ItemsAreEqual(encKey1, encKey2)) { rv = 1; goto done; }
+
+ done:
+ if (macKey1) SECITEM_ZfreeItem(macKey1, PR_TRUE);
+ if (macKey2) SECITEM_ZfreeItem(macKey2, PR_TRUE);
+ if (encKey1) SECITEM_ZfreeItem(encKey1, PR_TRUE);
+ if (encKey2) SECITEM_ZfreeItem(encKey2, PR_TRUE);
+
+ return rv;
+ }
+
+ // Private helper, retrieves the private key for the server
+ // from the database. Free the key using SECKEY_DestroyPrivateKey
+ int
+ Server::getPrivateKey(SECKEYPrivateKey **prvKey)
+ {
+ int rv = 0;
+ PK11SlotInfo *slot = 0;
+ SECKEYPrivateKeyList *list = 0;
+ SECKEYPrivateKeyListNode *n;
+ char *nickname;
+
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) goto done;
+
+ // ListPrivKeysInSlot looks like it should check the
+ // nickname and only return keys that match. However,
+ // that doesn't seem to work at the moment.
+ // BUG: XXXXX
+ list = PK11_ListPrivKeysInSlot(slot, mServerName, 0);
+ cout << "getPrivateKey: list = " << list << endl;
+ if (!list) { rv = 1; goto done; }
+
+ for(n = PRIVKEY_LIST_HEAD(list);
+ !PRIVKEY_LIST_END(n, list);
+ n = PRIVKEY_LIST_NEXT(n))
+ {
+ nickname = PK11_GetPrivateKeyNickname(n->key);
+ if (PL_strcmp(nickname, mServerName) == 0) break;
+ }
+ if (PRIVKEY_LIST_END(n, list)) { rv = 1; goto done; }
+
+ *prvKey = SECKEY_CopyPrivateKey(n->key);
+
+ done:
+ if (list) SECKEY_DestroyPrivateKeyList(list);
+
+ return rv;
+ }
+
+ int
+ Server::getPublicKey(SECKEYPublicKey **pubKey)
+ {
+ int rv;
+ SECKEYPrivateKey *prvKey = 0;
+
+ rv = getPrivateKey(&prvKey);
+ if (rv) goto done;
+
+ *pubKey = SECKEY_ConvertToPublicKey(prvKey);
+ if (!*pubKey) { rv = 1; goto done; }
+
+ done:
+ if (prvKey) SECKEY_DestroyPrivateKey(prvKey);
+
+ return rv;
+ }
+
+ int
+ Server::wrapKey(PK11SymKey *key, SECKEYPublicKey *pubKey, SECItem **ret)
+ {
+ int rv = 0;
+ SECItem *data;
+ SECStatus s;
+
+ data = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (!data) { rv = 1; goto done; }
+
+ // Allocate space for output of wrap
+ data->len = SECKEY_PublicKeyStrength(pubKey);
+ data->data = new unsigned char[data->len];
+ if (!data->data) { rv = 1; goto done; }
+
+ s = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, key, data);
+ if (s != SECSuccess) { rv = 1; goto done; }
+
+ *ret = data;
+ data = 0;
+
+ done:
+ if (data) SECITEM_FreeItem(data, PR_TRUE);
+
+ return rv;
+ }
+
+ // Example of how to do a raw export (no wrapping of a key)
+ // This should not be used. Use the RSA-based wrapping
+ // methods instead.
+ int
+ Server::rawExportKey(PK11SymKey *key, SECItem **res)
+ {
+ int rv = 0;
+ SECItem *data;
+ SECStatus s;
+
+ s = PK11_ExtractKeyValue(key);
+ if (s != SECSuccess) { rv = 1; goto done; }
+
+ data = PK11_GetKeyData(key);
+
+ *res = SECITEM_DupItem(data);
+ if (!*res) { rv = 1; goto done; }
+
+ done:
+ return rv;
+ }
+
+ // Initialize the NSS library. Normally this
+ // would be done as part of each server's startup.
+ // However, this example uses the same databases
+ // to store keys for server in the "cluster" so
+ // it is done once.
+ int
+ InitNSS()
+ {
+ int rv = 0;
+ SECStatus s;
+
+ s = NSS_InitReadWrite(".");
+ if (s != SECSuccess) rv = 1; // Error
+
+ // For this example, we don't use database passwords
+ PK11_InitPin(PK11_GetInternalKeySlot(), "", "");
+
+ return rv;
+ }
+
+ int
+ main(int argc, char *argv[])
+ {
+ int rv;
+ Server *server1 = 0;
+ Server *server2 = 0;
+
+ // Initialize NSS
+ rv = InitNSS();
+ if (rv) { cout << "InitNSS failed" << endl; goto done; }
+
+ // Create the first "server"
+ server1 = new Server("Server1");
+ if (!server1 || server1->Init())
+ {
+ cout << "Server1 could not be created" << endl;
+ rv = 1;
+ goto done;
+ }
+
+ // Generate encryption and mac keys. These keys will
+ // be used by all the servers in the cluster.
+ rv = server1->GenerateKeys();
+ if (rv) { cout << "GenerateKeys failed" << endl; goto done; }
+
+ // Now that everything is ready, start server1. This loads
+ // the encryption and MAC keys from the "files"
+ rv = server1->Start();
+ if (rv) { cout << "Cannot start server 1" << endl; goto done; }
+
+ // Create a second server in the cluster. We will need
+ // to transfer the keys from the first server to this
+ // one
+ server2 = new Server("Server2");
+ if (!server2 || server2->Init())
+ {
+ cout << "Server2 could not be created" << endl;
+ rv = 1; // Error
+ goto done;
+ }
+
+ // Transfer the keys from server1
+ {
+ SECItem *wrappedEncKey = 0;
+ SECItem *wrappedMacKey = 0;
+ SECItem *pubKeyData = 0;
+
+ // Get the public key for server 2 so that it can
+ // be sent to server 1
+ rv = server2->ExportPublicKey(&pubKeyData);
+ if (rv) { cout << "ExportPublicKey failed" << endl; goto trans_done; }
+
+ // Send the public key to server 1 and get back the
+ // wrapped key values
+ rv = server1->ExportKeys(pubKeyData, &wrappedEncKey, &wrappedMacKey);
+ if (rv) { cout << "ExportKeys failed" << endl; goto trans_done; }
+
+ // Print - for information
+ cout << "Wrapped Encryption Key" << endl;
+ printBuffer(wrappedEncKey->data, wrappedEncKey->len);
+ cout << "Wrapped MAC Key" << endl;
+ printBuffer(wrappedMacKey->data, wrappedMacKey->len);
+
+ // Import the keys into server 2 - this just puts the wrapped
+ // values into the "files"
+ rv = server2->ImportKeys(wrappedEncKey, wrappedMacKey);
+ if (rv) { cout << "ImportKeys failed" << endl; goto trans_done; }
+
+ trans_done:
+ if (wrappedEncKey) SECITEM_FreeItem(wrappedEncKey, PR_TRUE);
+ if (wrappedMacKey) SECITEM_FreeItem(wrappedMacKey, PR_TRUE);
+ if (pubKeyData) SECITEM_FreeItem(pubKeyData, PR_TRUE);
+ }
+ if (rv) goto done;
+
+ // Start server 2 - this unwraps the encryption and MAC keys
+ // so that they can be used
+ rv = server2->Start();
+ if (rv) { cout << "Cannot start server 2" << endl; goto done; }
+
+ // List keys in the token - informational
+ {
+ PK11SlotInfo *slot = 0;
+ SECKEYPrivateKeyList *list = 0;
+ SECKEYPrivateKeyListNode *n;
+
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) goto list_done;
+
+ cout << "List Private Keys" << endl;
+
+ list = PK11_ListPrivKeysInSlot(slot, 0, 0);
+ if (!list) goto list_done;
+
+ for(n = PRIVKEY_LIST_HEAD(list);
+ !PRIVKEY_LIST_END(n, list);
+ n = PRIVKEY_LIST_NEXT(n))
+ {
+ char *name;
+
+ name = PK11_GetPrivateKeyNickname(n->key);
+ cout << "Key: " << name << endl;
+ }
+ list_done:
+ if (slot) PK11_FreeSlot(slot);
+ if (list) SECKEY_DestroyPrivateKeyList(list);
+
+ cout << "Done" << endl;
+ }
+
+ // Let's see if the keys are the same
+ rv = server1->CompareKeys(server2);
+ if (rv) { cout << "Key Comparison failed" << endl; }
+
+ server1->Shutdown();
+ server2->Shutdown();
+
+ done:
+ if (server1) delete server1;
+ if (server2) delete server2;
+
+ NSS_Shutdown();
+
+ return rv;
+ } \ No newline at end of file