1.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample1: 2 3NSS Sample Code Sample1 4======================= 5 6.. _nss_sample_code_1_key_generation_and_transport_between_servers.: 7 8`NSS Sample Code 1: Key Generation and Transport Between Servers. <#nss_sample_code_1_key_generation_and_transport_between_servers.>`__ 9--------------------------------------------------------------------------------------------------------------------------------------- 10 11.. container:: 12 13 This is an example program that demonstrates how to do key generation and transport between 14 cooperating servers. This program shows the following: 15 16 - RSA key pair generation 17 - Naming RSA key pairs 18 - Looking up a previously generated key pair by name 19 - Creating AES and MAC keys (or encryption and MAC keys in general) 20 - Wrapping symmetric keys using your own RSA key pair so that they can be stored on disk or in a 21 database. 22 23 - As an alternative to TOKEN symmetric keys 24 25 - As a way to store large numbers of symmetric keys 26 27 - Wrapping symmetric keys using an RSA key from another server 28 - Unwrapping keys using your own RSA key pair 29 30 | The main part of the program shows a typical sequence of events for two servers that are trying 31 to extablish a shared key pair. 32 | We will add message protection (encryption and MACing) examples to this program in the future. 33 34.. _sample_code: 35 36`Sample Code <#sample_code>`__ 37~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 38 39.. container:: 40 41 .. code:: notranslate 42 43 #include <iostream.h> 44 #include "pk11pub.h" 45 #include "keyhi.h" 46 #include "nss.h" 47 48 // Key management for keys share among multiple hosts 49 // 50 // This example shows how to use NSS functions to create and 51 // distribute keys that need to be shared among multiple servers 52 // or hosts. 53 // 54 // The management scheme assumes that one host is PRIMARY. It 55 // generates the secret keys that will be used by all participating 56 // hosts. The other hosts (SECONDARY) request keys from the 57 // primary host. As an alternative, new keys may be sent to the 58 // current set of SECONDARY hosts when they are generated by the 59 // PRIMARY. In this case, the PRIMARY maintains a list of the 60 // secondary hosts. 61 // 62 // The sequence of events is: 63 // 1. The primary host generates a new symmetric key. This key 64 // may be used for an encryption mechanism (DES or AES) or for 65 // integrity (MD5_HMAC or SHA1_HMAC). This key needs to be 66 // permanent, since it may be used during several runs of the 67 // server. (Currently NSS doesn't store persistant keys. Steps 68 // 1a through 1x show how to do this). 69 // 1a. The primary host generates an RSA keypair that will be used 70 // store keys locally. 71 // 1b. The primary host wraps the newly generated key using the 72 // RSA key and stores the wrapped key data in a local file. 73 // 1c. The primary host unwraps the key using the RSA key each time 74 // access to the key is required, such as at server startup. 75 // 2. The secondary host generates an RSA keypair that will be used 76 // to transport keys between the primary host and itself. This 77 // key needs to exist long enough to be used to process the 78 // response to a key transport request that is made to the primary 79 // server. The example here shows how to create a permanent (token) 80 // RSA key for this purpose. (This key will also be used for 81 // storage of the keys, since NSS does not support permanent symmetric 82 // keys at the current time.) 83 // 3. The secondary host sends its RSA public key to the primary host as 84 // part of a request for a particular key, or to be added to a list 85 // of secondary hosts. 86 // 4. The administrator of the primary host verifies that the RSA key 87 // that was received belongs to a valid secondary host. The adminstrator 88 // may do this by checking that the key was received in a signed email 89 // message, or by checking a digest value with the adminstrator of the 90 // secondary host. [Need support for digest check values] 91 // 5. The primary host exports (wraps) the symmetric key using the 92 // secondary host's RSA key. The wrapped value is sent back to 93 // the secondary host. 94 // 6. The administrator of the secondary host verifies that the wrapped 95 // key data came from the primary host. The same methods outlined 96 // in step 4 may be used here. 97 // 7. The secondary host unwraps the key using its own RSA private key. 98 // NOTE: currently NSS does not support permanent symmetric keys. 99 // The secondary host may store the wrapped value that was received 100 // from the primary in a file, and unwrap it each time the key is required 101 // (such as at server startup). 102 103 // NSS actually has some support for permanent symmetric keys. However this 104 // example will need to be modified somewhat in order to demonstrate it. 105 106 // Utility function to print hex data 107 static void 108 printBuffer(unsigned char *digest, unsigned int len) 109 { 110 int i; 111 112 cout << "length: " << len << endl; 113 for(i = 0;i < len;i++) printf("%02x ", digest[i]); 114 cout << endl; 115 } 116 117 // XXX Data protection 118 // - takes an input buffer, applies the encryption 119 // and MAC, and generates a buffer with the result. 120 // - the application sends or uses the result (possibly 121 // after base64 encoding it. 122 123 // 124 // Server - an instance of a server that is part of a 125 // cluster of servers that are sharing a common set 126 // of encryption and MACing keys. 127 // 128 class Server 129 { 130 public: 131 // Initializes the server instance. In particular, this 132 // creates the key pair that is used for wrapping keys 133 int Init(); 134 135 // Generates keys for encryption (AES) and MACing. The 136 // wrapped keys are stored in data files. 137 int GenerateKeys(); 138 139 // Gets the server's public key (wrapping key) to 140 // send to another server. This becomes the input to 141 // the ExportKeys method on the remote server. 142 int ExportPublicKey(SECItem **pubKeyData); 143 144 // Export the encryption and key using the key 145 // provided. The key should come from another server 146 // in the cluster. (The admin should verify this.) 147 // 148 // In this example, the server must be started to perform 149 // this function (see Start()) 150 int ExportKeys(SECItem *pubKey, SECItem **wrappedEncKey, 151 SECItem **wrappedMacKey); 152 153 // Import the keys received from another server in the 154 // cluster. The admin should make sure the keys actually 155 // came from the correct source. 156 int ImportKeys(SECItem *wrappedEncKey, SECItem *wrappedMacKey); 157 158 // Start the server, loading the encryption and MACing keys 159 // from files 160 int Start(); 161 162 // Shut down the server. (For completeness) 163 int Shutdown(); 164 165 // Compare keys in two server instances. Use this in the 166 // example to make sure the keys are transferred correctly. 167 // This will not work in real life! 168 // 169 // The servers must be started 170 int CompareKeys(Server *peer); 171 172 // Create a server - the name distiguish the keys in the 173 // shared database in this example 174 Server(const char *serverName); 175 ~Server(); 176 177 private: 178 int getPrivateKey(SECKEYPrivateKey **prvKey); 179 int getPublicKey(SECKEYPublicKey **pubKey); 180 int wrapKey(PK11SymKey *key, SECKEYPublicKey *pubKey, SECItem **data); 181 182 // export raw key (unwrapped) DO NOT USE 183 int rawExportKey(PK11SymKey *key, SECItem **data); 184 185 char *mServerName; 186 187 // These items represent data that might be stored 188 // in files or in a configuration file 189 SECItem *mWrappedEncKey; 190 SECItem *mWrappedMacKey; 191 192 // These are the runtime keys as loaded from the files 193 PK11SymKey *mEncKey; 194 PK11SymKey *mMacKey; 195 }; 196 197 Server::Server(const char *serverName) 198 : mServerName(0), mWrappedEncKey(0), mWrappedMacKey(0), 199 mEncKey(0), mMacKey(0) 200 { 201 // Copy the server name 202 mServerName = PL_strdup(serverName); 203 } 204 205 Server::~Server() 206 { 207 if (mServerName) PL_strfree(mServerName); 208 if (mWrappedEncKey) SECITEM_FreeItem(mWrappedEncKey, PR_TRUE); 209 if (mWrappedMacKey) SECITEM_FreeItem(mWrappedMacKey, PR_TRUE); 210 if (mEncKey) PK11_FreeSymKey(mEncKey); 211 if (mMacKey) PK11_FreeSymKey(mMacKey); 212 } 213 214 int 215 Server::Init() 216 { 217 int rv = 0; 218 SECKEYPrivateKey *prvKey = 0; 219 SECKEYPublicKey *pubKey = 0; 220 PK11SlotInfo *slot = 0; 221 PK11RSAGenParams rsaParams; 222 SECStatus s; 223 224 // See if there is already a private key with this name. 225 // If there is one, no further action is required. 226 rv = getPrivateKey(&prvKey); 227 if (rv == 0 && prvKey) goto done; 228 229 rv = 0; 230 231 // These could be parameters to the Init function 232 rsaParams.keySizeInBits = 1024; 233 rsaParams.pe = 65537; 234 235 slot = PK11_GetInternalKeySlot(); 236 if (!slot) { rv = 1; goto done; } 237 238 prvKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams, 239 &pubKey, PR_TRUE, PR_TRUE, 0); 240 if (!prvKey) { rv = 1; goto done; } 241 242 // Set the nickname on the private key so that it 243 // can be found later. 244 s = PK11_SetPrivateKeyNickname(prvKey, mServerName); 245 if (s != SECSuccess) { rv = 1; goto done; } 246 247 done: 248 if (slot) PK11_FreeSlot(slot); 249 if (pubKey) SECKEY_DestroyPublicKey(pubKey); 250 if (prvKey) SECKEY_DestroyPrivateKey(prvKey); 251 252 return rv; 253 } 254 255 int 256 Server::GenerateKeys() 257 { 258 int rv = 0; 259 SECKEYPublicKey *pubKey = 0; 260 PK11SlotInfo *slot = 0; 261 262 // Choose a slot to use 263 slot = PK11_GetInternalKeySlot(); 264 if (!slot) { rv = 1; goto done; } 265 266 // Get our own public key to use for wrapping 267 rv = getPublicKey(&pubKey); 268 if (rv) goto done; 269 270 // Do the Encryption (AES) key 271 if (!mWrappedEncKey) 272 { 273 PK11SymKey *key = 0; 274 275 // The key size is 128 bits (16 bytes) 276 key = PK11_KeyGen(slot, CKM_AES_KEY_GEN, 0, 128/8, 0); 277 if (!key) { rv = 1; goto aes_done; } 278 279 rv = wrapKey(key, pubKey, &mWrappedEncKey); 280 281 aes_done: 282 if (key) PK11_FreeSymKey(key); 283 284 if (rv) goto done; 285 } 286 287 // Do the Mac key 288 if (!mWrappedMacKey) 289 { 290 PK11SymKey *key = 0; 291 292 // The key size is 160 bits (20 bytes) 293 key = PK11_KeyGen(slot, CKM_GENERIC_SECRET_KEY_GEN, 0, 160/8, 0); 294 if (!key) { rv = 1; goto mac_done; } 295 296 rv = wrapKey(key, pubKey, &mWrappedMacKey); 297 298 mac_done: 299 if (key) PK11_FreeSymKey(key); 300 } 301 302 done: 303 if (slot) PK11_FreeSlot(slot); 304 305 return rv; 306 } 307 308 int 309 Server::ExportPublicKey(SECItem **pubKeyData) 310 { 311 int rv = 0; 312 SECKEYPublicKey *pubKey = 0; 313 314 rv = getPublicKey(&pubKey); 315 if (rv) goto done; 316 317 *pubKeyData = SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey); 318 if (!*pubKeyData) { rv = 1; goto done; } 319 320 done: 321 if (pubKey) SECKEY_DestroyPublicKey(pubKey); 322 323 return rv; 324 } 325 326 int 327 Server::ExportKeys(SECItem *pubKeyData, SECItem **wrappedEncKey, 328 SECItem **wrappedMacKey) 329 { 330 int rv; 331 CERTSubjectPublicKeyInfo *keyInfo = 0; 332 SECKEYPublicKey *pubKey = 0; 333 SECItem *data = 0; 334 335 // Make sure the keys are available (server running) 336 if (!mEncKey || !mMacKey) { rv = 1; goto done; } 337 338 // Import the public key of the other server 339 keyInfo = SECKEY_DecodeDERSubjectPublicKeyInfo(pubKeyData); 340 if (!keyInfo) { rv = 1; goto done; } 341 342 pubKey = SECKEY_ExtractPublicKey(keyInfo); 343 if (!pubKey) { rv = 1; goto done; } 344 345 // Export the encryption key 346 rv = wrapKey(mEncKey, pubKey, &data); 347 if (rv) goto done; 348 349 // Export the MAC key 350 rv = wrapKey(mMacKey, pubKey, wrappedMacKey); 351 if (rv) goto done; 352 353 // Commit the rest of the operation 354 *wrappedEncKey = data; 355 data = 0; 356 357 done: 358 if (data) SECITEM_FreeItem(data, PR_TRUE); 359 if (pubKey) SECKEY_DestroyPublicKey(pubKey); 360 if (keyInfo) SECKEY_DestroySubjectPublicKeyInfo(keyInfo); 361 362 return rv; 363 } 364 365 int 366 Server::ImportKeys(SECItem *wrappedEncKey, SECItem *wrappedMacKey) 367 { 368 int rv = 0; 369 370 if (mWrappedEncKey || mWrappedMacKey) { rv = 1; goto done; } 371 372 mWrappedEncKey = SECITEM_DupItem(wrappedEncKey); 373 if (!mWrappedEncKey) { rv = 1; goto done; } 374 375 mWrappedMacKey = SECITEM_DupItem(wrappedMacKey); 376 if (!mWrappedMacKey) { rv = 1; goto done; } 377 378 done: 379 return rv; 380 } 381 382 int 383 Server::Start() 384 { 385 int rv; 386 SECKEYPrivateKey *prvKey = 0; 387 388 rv = getPrivateKey(&prvKey); 389 if (rv) goto done; 390 391 if (!mEncKey) 392 { 393 // Unwrap the encryption key from the "file" 394 // This function uses a mechanism rather than a key type 395 // Does this need to be "WithFlags"?? 396 mEncKey = PK11_PubUnwrapSymKey(prvKey, mWrappedEncKey, 397 CKM_AES_CBC_PAD, CKA_ENCRYPT, 0); 398 if (!mEncKey) { rv = 1; goto done; } 399 } 400 401 if (!mMacKey) 402 { 403 // Unwrap the MAC key from the "file" 404 // This function uses a mechanism rather than a key type 405 // Does this need to be "WithFlags"?? 406 mMacKey = PK11_PubUnwrapSymKey(prvKey, mWrappedMacKey, 407 CKM_MD5_HMAC, CKA_SIGN, 0); 408 if (!mMacKey) { rv = 1; goto done; } 409 } 410 411 done: 412 if (prvKey) SECKEY_DestroyPrivateKey(prvKey); 413 414 return rv; 415 } 416 417 int 418 Server::Shutdown() 419 { 420 if (mEncKey) PK11_FreeSymKey(mEncKey); 421 if (mMacKey) PK11_FreeSymKey(mMacKey); 422 423 mEncKey = 0; 424 mMacKey = 0; 425 426 return 0; 427 } 428 429 int 430 Server::CompareKeys(Server *peer) 431 { 432 int rv; 433 SECItem *macKey1 = 0; 434 SECItem *macKey2 = 0; 435 SECItem *encKey1 = 0; 436 SECItem *encKey2 = 0; 437 438 // Export each of the keys in raw form 439 rv = rawExportKey(mMacKey, &macKey1); 440 if (rv) goto done; 441 442 rv = rawExportKey(peer->mMacKey, &macKey2); 443 if (rv) goto done; 444 445 rv = rawExportKey(mEncKey, &encKey1); 446 if (rv) goto done; 447 448 rv = rawExportKey(peer->mEncKey, &encKey2); 449 if (rv) goto done; 450 451 if (!SECITEM_ItemsAreEqual(macKey1, macKey2)) { rv = 1; goto done; } 452 if (!SECITEM_ItemsAreEqual(encKey1, encKey2)) { rv = 1; goto done; } 453 454 done: 455 if (macKey1) SECITEM_ZfreeItem(macKey1, PR_TRUE); 456 if (macKey2) SECITEM_ZfreeItem(macKey2, PR_TRUE); 457 if (encKey1) SECITEM_ZfreeItem(encKey1, PR_TRUE); 458 if (encKey2) SECITEM_ZfreeItem(encKey2, PR_TRUE); 459 460 return rv; 461 } 462 463 // Private helper, retrieves the private key for the server 464 // from the database. Free the key using SECKEY_DestroyPrivateKey 465 int 466 Server::getPrivateKey(SECKEYPrivateKey **prvKey) 467 { 468 int rv = 0; 469 PK11SlotInfo *slot = 0; 470 SECKEYPrivateKeyList *list = 0; 471 SECKEYPrivateKeyListNode *n; 472 char *nickname; 473 474 slot = PK11_GetInternalKeySlot(); 475 if (!slot) goto done; 476 477 // ListPrivKeysInSlot looks like it should check the 478 // nickname and only return keys that match. However, 479 // that doesn't seem to work at the moment. 480 // BUG: XXXXX 481 list = PK11_ListPrivKeysInSlot(slot, mServerName, 0); 482 cout << "getPrivateKey: list = " << list << endl; 483 if (!list) { rv = 1; goto done; } 484 485 for(n = PRIVKEY_LIST_HEAD(list); 486 !PRIVKEY_LIST_END(n, list); 487 n = PRIVKEY_LIST_NEXT(n)) 488 { 489 nickname = PK11_GetPrivateKeyNickname(n->key); 490 if (PL_strcmp(nickname, mServerName) == 0) break; 491 } 492 if (PRIVKEY_LIST_END(n, list)) { rv = 1; goto done; } 493 494 *prvKey = SECKEY_CopyPrivateKey(n->key); 495 496 done: 497 if (list) SECKEY_DestroyPrivateKeyList(list); 498 499 return rv; 500 } 501 502 int 503 Server::getPublicKey(SECKEYPublicKey **pubKey) 504 { 505 int rv; 506 SECKEYPrivateKey *prvKey = 0; 507 508 rv = getPrivateKey(&prvKey); 509 if (rv) goto done; 510 511 *pubKey = SECKEY_ConvertToPublicKey(prvKey); 512 if (!*pubKey) { rv = 1; goto done; } 513 514 done: 515 if (prvKey) SECKEY_DestroyPrivateKey(prvKey); 516 517 return rv; 518 } 519 520 int 521 Server::wrapKey(PK11SymKey *key, SECKEYPublicKey *pubKey, SECItem **ret) 522 { 523 int rv = 0; 524 SECItem *data; 525 SECStatus s; 526 527 data = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); 528 if (!data) { rv = 1; goto done; } 529 530 // Allocate space for output of wrap 531 data->len = SECKEY_PublicKeyStrength(pubKey); 532 data->data = new unsigned char[data->len]; 533 if (!data->data) { rv = 1; goto done; } 534 535 s = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, key, data); 536 if (s != SECSuccess) { rv = 1; goto done; } 537 538 *ret = data; 539 data = 0; 540 541 done: 542 if (data) SECITEM_FreeItem(data, PR_TRUE); 543 544 return rv; 545 } 546 547 // Example of how to do a raw export (no wrapping of a key) 548 // This should not be used. Use the RSA-based wrapping 549 // methods instead. 550 int 551 Server::rawExportKey(PK11SymKey *key, SECItem **res) 552 { 553 int rv = 0; 554 SECItem *data; 555 SECStatus s; 556 557 s = PK11_ExtractKeyValue(key); 558 if (s != SECSuccess) { rv = 1; goto done; } 559 560 data = PK11_GetKeyData(key); 561 562 *res = SECITEM_DupItem(data); 563 if (!*res) { rv = 1; goto done; } 564 565 done: 566 return rv; 567 } 568 569 // Initialize the NSS library. Normally this 570 // would be done as part of each server's startup. 571 // However, this example uses the same databases 572 // to store keys for server in the "cluster" so 573 // it is done once. 574 int 575 InitNSS() 576 { 577 int rv = 0; 578 SECStatus s; 579 580 s = NSS_InitReadWrite("."); 581 if (s != SECSuccess) rv = 1; // Error 582 583 // For this example, we don't use database passwords 584 PK11_InitPin(PK11_GetInternalKeySlot(), "", ""); 585 586 return rv; 587 } 588 589 int 590 main(int argc, char *argv[]) 591 { 592 int rv; 593 Server *server1 = 0; 594 Server *server2 = 0; 595 596 // Initialize NSS 597 rv = InitNSS(); 598 if (rv) { cout << "InitNSS failed" << endl; goto done; } 599 600 // Create the first "server" 601 server1 = new Server("Server1"); 602 if (!server1 || server1->Init()) 603 { 604 cout << "Server1 could not be created" << endl; 605 rv = 1; 606 goto done; 607 } 608 609 // Generate encryption and mac keys. These keys will 610 // be used by all the servers in the cluster. 611 rv = server1->GenerateKeys(); 612 if (rv) { cout << "GenerateKeys failed" << endl; goto done; } 613 614 // Now that everything is ready, start server1. This loads 615 // the encryption and MAC keys from the "files" 616 rv = server1->Start(); 617 if (rv) { cout << "Cannot start server 1" << endl; goto done; } 618 619 // Create a second server in the cluster. We will need 620 // to transfer the keys from the first server to this 621 // one 622 server2 = new Server("Server2"); 623 if (!server2 || server2->Init()) 624 { 625 cout << "Server2 could not be created" << endl; 626 rv = 1; // Error 627 goto done; 628 } 629 630 // Transfer the keys from server1 631 { 632 SECItem *wrappedEncKey = 0; 633 SECItem *wrappedMacKey = 0; 634 SECItem *pubKeyData = 0; 635 636 // Get the public key for server 2 so that it can 637 // be sent to server 1 638 rv = server2->ExportPublicKey(&pubKeyData); 639 if (rv) { cout << "ExportPublicKey failed" << endl; goto trans_done; } 640 641 // Send the public key to server 1 and get back the 642 // wrapped key values 643 rv = server1->ExportKeys(pubKeyData, &wrappedEncKey, &wrappedMacKey); 644 if (rv) { cout << "ExportKeys failed" << endl; goto trans_done; } 645 646 // Print - for information 647 cout << "Wrapped Encryption Key" << endl; 648 printBuffer(wrappedEncKey->data, wrappedEncKey->len); 649 cout << "Wrapped MAC Key" << endl; 650 printBuffer(wrappedMacKey->data, wrappedMacKey->len); 651 652 // Import the keys into server 2 - this just puts the wrapped 653 // values into the "files" 654 rv = server2->ImportKeys(wrappedEncKey, wrappedMacKey); 655 if (rv) { cout << "ImportKeys failed" << endl; goto trans_done; } 656 657 trans_done: 658 if (wrappedEncKey) SECITEM_FreeItem(wrappedEncKey, PR_TRUE); 659 if (wrappedMacKey) SECITEM_FreeItem(wrappedMacKey, PR_TRUE); 660 if (pubKeyData) SECITEM_FreeItem(pubKeyData, PR_TRUE); 661 } 662 if (rv) goto done; 663 664 // Start server 2 - this unwraps the encryption and MAC keys 665 // so that they can be used 666 rv = server2->Start(); 667 if (rv) { cout << "Cannot start server 2" << endl; goto done; } 668 669 // List keys in the token - informational 670 { 671 PK11SlotInfo *slot = 0; 672 SECKEYPrivateKeyList *list = 0; 673 SECKEYPrivateKeyListNode *n; 674 675 slot = PK11_GetInternalKeySlot(); 676 if (!slot) goto list_done; 677 678 cout << "List Private Keys" << endl; 679 680 list = PK11_ListPrivKeysInSlot(slot, 0, 0); 681 if (!list) goto list_done; 682 683 for(n = PRIVKEY_LIST_HEAD(list); 684 !PRIVKEY_LIST_END(n, list); 685 n = PRIVKEY_LIST_NEXT(n)) 686 { 687 char *name; 688 689 name = PK11_GetPrivateKeyNickname(n->key); 690 cout << "Key: " << name << endl; 691 } 692 list_done: 693 if (slot) PK11_FreeSlot(slot); 694 if (list) SECKEY_DestroyPrivateKeyList(list); 695 696 cout << "Done" << endl; 697 } 698 699 // Let's see if the keys are the same 700 rv = server1->CompareKeys(server2); 701 if (rv) { cout << "Key Comparison failed" << endl; } 702 703 server1->Shutdown(); 704 server2->Shutdown(); 705 706 done: 707 if (server1) delete server1; 708 if (server2) delete server2; 709 710 NSS_Shutdown(); 711 712 return rv; 713 }