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      }