diff options
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.rst | 713 |
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 |