Netscape Communicator Key Database Format.

Introduction.

Firstly a bit of background.

I discovered the format of the Netscape communicator private key database some time ago. Since then I've been asked from time to time for more details. Since this information isn't usually needed and there were some security concerns I have not revealed this information until now, also it took a considerable amount of work to determine the format used.

Since then some things have changed. Firstly the number of requests has increased enormously and secondly Netscape have themselves released code to access their databases, this is now part of the open source NSS library in the Mozilla project. Check the Mozilla web site for more information, including an official description of the key derivation algorithm.

This information was used to in some database access code I wrote  for Celo Communications and they have kindly permitted me to make the details public.

This information relates to the format used by Communicator 4.X, 4.5X and 4.7X. Older Netscape servers and version 3.0 of Navigator use a different format: information about this format will be made available in due course.

Basic Details.

The Netscape key database is contained in a file called key3.db. Like the certificate database it is in Berkeley DB 1.85 hash format, see the certificate database page for more information.

Record Structure.

There are four different types of record. A version entry, a global salt entry, a password check entry and zero or more private key entries. If the database is newly created then it will have no private key entries at all. Note: the database will have a hybrid format if it is newly converted from Netscape Communicator 3.0, it will stay in this format until the first time the password is entered (since this is needed to read the entries), then it will be converted to 4.0 format.

These entries are distinguished by their database keys. They are described in more detail below.

Version entry.

This entry has the 7 byte ASCII string Version as its key, not null terminated. It is used for version information and consists of the single byte 0x3 in the versions I tested.

Global Salt.

This is an entry with the 11 byte key global-salt again not null terminated. It is used to derive the decryption keys from passwords. It's value is 16 bytes long.

Password Check.

This entry has the 14 byte key password-check.

Here is a sample value:

0000 03 10 01 15 96 bb 81 12 65 2a 43 e7 bd fb 2f dc  |........e*C.../.|
0010 87 99 e5 00 0b 2a 86 48 86 f7 0d 01 0c 05 01 03  |.....*.H........|
0020 c0 84 68 48 fe 6e 35 24 fd d4 a6 e3 e7 83 cf 38  |..hH.n5$.......8|

The first three bytes are header information. This is followed by an entry salt the length of which is specified in the second byte.
In this example the entry salt is of length 0x10 and starts with the value 0x15.

The last 16 bytes is the string password-check encrypted using triple DES in CBC mode and standard block padding. The key used is derived from the global salt and the entry salt. By checking that this entry can be decrypted correctly the password can be checked.

[Side note: I was a little disappointed when I found out what was encrypted here. I was expecting some insulting comment about MSIE :-)]

Private Key entries.

The private key entries has a database key that is part of the public key, for an RSA key it is the modulus, for a DSA key it is the public value both are represented as big endian numbers in ASN1 format. That is an extra zero byte is prepended if the first byte is 0x80 or more.

Here is a sample value:

0000 03 08 05 3f 29 2b 8e 17 15 64 76 74 65 73 74 00  |...?)+...dvtest.|
0010 30 82 01 82 30 1c 06 0b 2a 86 48 86 f7 0d 01 0c  |0...0...*.H.....|
0020 05 01 03 30 0d 04 08 3f 29 2b 8e 17 15 64 76 02  |...0...?)+...dv.|
[remaining data omitted]

The first three bytes are header information. Following it is an entry salt whose length is given by the second byte. In this case it is of length 0x08 and consists of the bytes: 0x3f through 0x76. Following this is the name of the certificate (which may be empty) which appears in the list box as a null terminated ASCII string, the length of this is given by the third byte. In this example it is "test". The remainder of the entry is the private key itself.

The private key is in PKCS#8 format and looks like this for the example above.

  16 30  386: SEQUENCE {
  20 30   28:   SEQUENCE {
  22 06   11:     OBJECT IDENTIFIER
            :       pkcs-12-PBEWithSha1AndTripleDESCBC (1 2 840 113549 1 12 5 1 3)
  35 30   13:     SEQUENCE {
  37 04    8:       OCTET STRING
            :         3F 29 2B 8E 17 15 64 76
  47 02    1:       INTEGER 1
            :       }
            :     }
  50 04  352:   OCTET STRING
            :     70 33 C0 A1 CD 50 CA 39 56 3A B0 ED 3D 46 6B 75
            :     A7 5A B7 A5 9F 9A D7 45 25 EC 12 2E 41 4D 51 58
            :     29 FC 43 72 67 C9 36 10 10 0F 1C 54 00 4E 66 3D
            :     C1 63 7F F9 D6 5F 0E D4 9F D1 5E 0C 24 EB DA FF
            :     D5 41 D9 A0 F9 81 8D BD BA EA E9 19 3E 58 A0 EA
            :     AF A5 B9 03 EE 90 8F A1 B7 ED 7E 4E 28 67 38 89
            :     65 CB 17 DB 00 0E 2A 37 42 AD 8D BA F5 3A FD ED
            :     B2 53 A2 5A D2 B0 0C 7E F9 01 E1 50 15 B9 A2 FC
            :             [ Another 224 bytes skipped ]
            :   }

The AlgorithmIdentifier used here is a PKCS#5 PBEParameter structure. The more observant may recognize the salt: it is exactly the same as the entry salt. The object identifier representing the algorithm is one of the original PFX ones: but the key derivation algorithm is different. The encryption algorithm is triple DES in CBC mode with standard block padding.

The decrypted private key is, as might be expected, a PKCS#8 PrivateKeyInfo structure. However it is non standard for DSA keys: the privateKey contents is an ASN1 SEQUENCE consisting of the private and public keys respectively expressed as ASN1 INTEGERs, rather than the standard single INTEGER containing the private key. I suppose when this stuff was written there wasn't a PKCS#8 DSA standard (which is currently well hidden in PKCS#11).

Key Derivation Algorithm.

This is of course all very well but it is useless if you can't decrypt the private key entries. The key derivation algorithm is a very peculiar beast, AFAIK it is a Netscape invention used for nothing else. This description has been independently implemented and verified by several people so I'm pretty sure it is accurate.

Anyway here is the info.

Initially you will need the database password, this is represented as an ASCII string: the null terminator is not included in any operations.

Form the hashed password HP:

HP = SHA1(global-salt||password).

Note: at this point the password can be deleted from memory: this avoids having to keep plaintext passwords in memory. All operations can be done just using HP.

Call the entry salt ES. Now append zero bytes to ES until the result is 20 bytes long to form a padded entry salt PES.

Form a combined hashed password CHP:

CHP = SHA1(HP||ES)

Now it gets a bit more complicated. The following operations use an SHA1 HMAC using CHP as the key. This will be represented as CHMAC(text)

Form the key k1:

k1 = CHMAC(PES||ES)

Form the temporary key tk:

tk = CHMAC(PES)

Form the key k2 using:

k2 = CHMAC(tk||ES)

Finally form the 40 byte key k:

k = k1||k2

The first 24 bytes of k are used to form a 3DES key and the the last 8 bytes are used for the IV.

That's the complete key derivation algorithm, you're probably as surprised as I am that this wasn't guessed before :-)

Security Concerns.

As with many password based encryption schemes there are security concerns. In this case the main concern is that the algorithm does not have a high iteration count. This makes dictionary attacks on the password possible.

Test Vectors.

I'll take pity of those that may have to implement this algorithm and provide some test vectors. Here is a sample session on decrypting the password-check entry from the example above. The password is the 8 character string "password" (without the quotes). All output is in hex.

The password is:
70 61 73 73 77 6f 72 64

The global salt is:
5a ac 8e 04 39 e8 d6 9e a0 fe 1b c0 13 cd 5a f8

The entry salt ES is:
15 96 bb 81 12 65 2a 43 e7 bd fb 2f dc 87 99 e5

And the data to be decrypted is:
c0 84 68 48 fe 6e 35 24 fd d4 a6 e3 e7 83 cf 38

Now the actual results:

PES =  15 96 bb 81 12 65 2a 43 e7 bd fb 2f dc 87 99 e5 00 00 00 00
HP =   57 f3 86 39 12 8e 14 89 1b 18 51 8f 63 be c6 cb 56 73 82 77
CHP =  8e 1e 3a 52 36 ab c3 44 55 2d 6e 1a 00 67 1a ed 69 2d 48 25
k1 =   16 74 39 40 5b 76 bd cb 62 ea b2 1a 71 e5 59 12 9c f2 cb 6d
tk =   c3 34 9d 3c ec 3e 83 55 62 4c af 06 f2 6b 69 bc 17 0a 21 0e
k2 =   4d c5 0b 9c ad f1 53 78 a0 bc 86 11 90 75 b3 de 65 c7 d8 a7
k =    16 74 39 40 5b 76 bd cb 62 ea b2 1a 71 e5 59 12 9c f2 cb 6d
       4d c5 0b 9c ad f1 53 78 a0 bc 86 11 90 75 b3 de 65 c7 d8 a7
key =  16 74 39 40 5b 76 bd cb 62 ea b2 1a 71 e5 59 12 9c f2 cb 6d 4d c5 0b 9c
iv  =  90 75 b3 de 65 c7 d8 a7

Any comments to the address on my contact page.