# Keys and Formats

Keys and key formats are a popular topic on the Crypto++ mailing list. The topics range from what format is the key in, to how does one save and load a key. Topics on this page will include frequently re-occurring answers offered by folks like Geoff Beier.

Though this page discusses `RSA`

and `DSA`

keys in particular, the information applies equally to all Crypto++ asymmetric keys. This is due to the library's use of inheritance (for objects which derive from `PublicKey`

and `PrivateKey`

) and composition (for objects which derive from `PublicKeyAlgorithm`

and `PrivateKeyAlgorithm`

). Samples can be found with other systems - for example Elliptic Curve Integrated Encryption Scheme covers operations on ECIES keys.

In general, we should use the key's `Load`

and `Save`

functions to achieve maximum interoperability with other libraries such as OpenSSL, Java, and Microsoft's .Net. `Load`

and `Save`

operate on PKCS #8's *PrivateKeyInfo* and X.509's *SubjectPublicKeyInfo*. Note that X.509, IETF's Privacy Enhanced Mail (PEM), and PKCS #1 (RSA Encryption Standard) are identical in their definitions of public RSA keys.

If the keys will stay within the program's boundaries, we can use PKCS #8's *PrivateKey* (part of *PrivateKeyInfo*) and X.509's *PublicKey* (part of *SubjectPublicKeyInfo*). PKCS #8's *PrivateKey* and X.509's *PublicKey* save on overhead of bytes and processing time at the cost of interoperability.

ASN.1 must be used when writing keys using X.509 and PKCS #8. In particular the keys must be written using DER encoding and subsequently read using BER encoding. BER is a bit sloppier than DER - the standard's intent was to 'write strict' and 'read loose' to promote interoperability. See *X.690, Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)*.

Finally, two Code Project articles covering Crypto++, keys, and interoperability are available. See Cryptographic Interoperability: Keys and Cryptographic Interoperability: Digital Signatures.

## Crypto++ Key Formats

Internally, Crypto++ uses a key representation that follows the real world object of interest. However, a raw byte dump of a Crypto++ key object is generally useless outside of the library. For example, Crypto++ **does not** represent a private key in PKCS #8 format with an ASN.1 encoding, so a dump is not useful for interoperability. A Crypto++ key **does** offer methods which allow the library to work with many popular formats, including PKCS #8 and X.509.

### Private Key Format

The external private key format used by Crypto++ is *PKCS #8: Private-Key Information Syntax Standard*. The PKCS standard dictates an ASN.1 encoding of the key. ASN.1 is Abstract Syntax Notation and is specified in ITU's *X.690, Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)*.

Conceptually, there are two parts to the format. The 'first' part is packaging information such as an OID, version, and attributes. The 'second' part is the actual serialized key bits. In PKCS #8, the 'first' part is called *PrivateKeyInfo*, while the second part is called a *PrivateKey*.

In practice, we should think of the 'second ' part as *XxxPrivateKey*, where the 'Xxx' will be set later once the OID is known. For example, if the OID is that of `rsaEncryption`

, *XxxPrivateKey* becomes *RsaPrivateKey*. If the OID is `dsa`

, then *XxxPrivateKey* becomes *DsaPrivateKey*.

The depiction of the PrivateKey shows sample data `00 B7 67 B4 ...`

. The sample data is encoded using an ASN.1 *Octet String*. Note that the PrivateKeyInfo encompasses the PrivateKey (they are not laid out sequentially).

### Public Key Format

The external public key format used by Crypto++ is *X.509: Public-key and attribute certificate frameworks*. As with PKCS #8, keys are encoded using ASN.1.

Conceptually, there are two parts to the format. The 'first' part is packaging with information such as an OID. The 'second' part is the actual serialized key bits (it sounds a lot like a PKCS #8 all over again). In X.509, the 'first' part is called *PublicKeyInfo*, while the second part is called a *PublicKey*. As with *XxxPrivateKey*, we should expect to nail down a key type once the OID is known so that *XxxPublicKey* becomes *RsaPublicKey*, *DsaPublicKey* (etc.).

The PublicKey diverges from PKCS #8 in the ASN.1 data type used to encode the PublicKey. The depiction of the PublicKey shows sample data of `10101010111...`

. The sample data is encoded using an ASN.1 *Binary String*. As with PKCS #8, the PublicKeyInfo encompasses the PublicKey (they are not laid out sequentially).

### Crypto++ PKCS8PrivateKey

`RSA::PrivateKey`

and `DSA::PrivateKey`

are the Crypto++ objects which encapsulate much of the RSA and DSA standards keys (ie, PKCS, X.509, IEEE, and IETF). Each inherit from a `PrivateKey`

and more importantly a `PKCS8PrivateKey`

. The documentation for `PKCS8PrivateKey`

class is located in the PKCS8PrivateKey Class Reference and shows the class offers `Load`

and `Save`

functions.

### Crypto++ X509PublicKey

Similarly, `RSA::PublicKey`

and `DSA::PublicKey`

each inherit from a `PublicKey`

and `X509PublicKey`

. The documentation for the `X509PublicKey`

class is located in the X509PublicKey Class Reference and shows the class offers `Load`

and `Save`

functions.

## Generating, Validating, Saving, and Loading Keys

We will need a program to generate both an RSA key pair and DSA key pairs. For that, we will use cryptopp-key-gen. cryptopp-key-gen generates and saves the keys for demonstration purposes. The RSA keys can be used for encryption (see RSA Encryption Schemes) or signatures (see RSA Signature Schemes); and the DSA key are used with the Digital Signature Algorithm.

**Note**: `RSA::PrivateKey`

and `DSA::PrivateKey`

are the Crypto++ equivalent of PKCS #8 *PrivateKeyInfo* type. Similarly, `RSA::PublicKey`

and `DSA::PublicKey`

are the X.509 *PublicKeyInfo* type. If you want to save or load a key in PKCS #8 *XxxPrivateKey* format or X.509 *XxxPublicKey* format, see BER and DER Encoding below.

//////////////////////////////////////////////////////////////////////////////////// RSA::PrivateKey rsaPrivate; rsaPrivate.GenerateRandomWithKeySize(prng, 3072); RSA::PublicKey rsaPublic(rsaPrivate); SavePrivateKey("rsa-private.key", rsaPrivate); SavePublicKey("rsa-public.key", rsaPublic); //////////////////////////////////////////////////////////////////////////////////// DSA::PrivateKey dsaPrivate; dsaPrivate.GenerateRandomWithKeySize(prng, 1024); DSA::PublicKey dsaPublic; dsaPrivate.MakePublicKey(dsaPublic); SavePrivateKey("dsa-private.key", dsaPrivate); SavePublicKey("dsa-public.key", dsaPublic);

### Generating Keys

Key generation will for RSA and DSA are very similar. `RSA::PrivateKey`

and `DSA::PrivateKey`

each inherit from Crypto++'s `PrivateKey`

(documented at PrivateKey Class Reference), which offers `GenerateRandom`

and `GenerateRandomWithKeySize`

. Both Crypto++ methods take a `RandomNumberGenerator`

(documented at RandomNumberGenerator Class Reference), and the later takes a generator and bit size.

When choosing a bit size, current best practices (from NIST) dictates we use a security level of 128 (80 and 112 bits of security should not be used in new systems). This means we should use an RSA modulus size of 3072 bits. This also means that a DSS version 1 or version 2 DSA key pairs are too weak to satisfy contemporary security requirements (a 1024 bit DSA key offers about 80 ideal bits of security).

In the code below, we create an RSA key of 3072 bits, and a DSA key of 1024 bits (per DSS Version 2). `AutoSeededRandomPool`

is a PGP Random Pool-like generator. If you need more security in a generator, use an `X917RNG`

(from ANSI 9.17 Appendix C and based on DES) or `AutoSeededX917RNG`

based on the `AES`

block cipher.

AutoSeededRandomPool prng; RSA::PrivateKey rsaPrivate; rsaPrivate.GenerateRandomWithKeySize(prng, 3072); DSA::PrivateKey dsaPrivate; dsaPrivate.GenerateRandomWithKeySize(prng, 1024);

We create public keys from the private keys as follows. The `RSA::PublicKey`

allows us to use the private key in the constructor. The `DSA::PublicKey`

requires we use `MakePublicKey`

.

RSA::PublicKey rsaPublic(rsaPrivate); DSA::PublicKey dsaPublic; dsaPrivate.MakePublicKey(dsaPublic);

### Saving Keys

Crypto++ is full of self documenting source code^{TM}. If we search the sources for ".Save" (notice the period since its a method), we find at least 25 files which use the function. Of particular interest is test.cpp, which is part of *cryptest.exe*, and which we know will generate and save keys. Unfortunately, `GenerateRSAKey`

writes a hex encoded key, which does not promote poking and prodding by tools such as *dumpasn1*.

Moving on to validate2.cpp, we find a lot of loading and saving into byte queues. A `ByteQueue`

is a good choice for a sink since it uses a secure allocator - SecByteBlock - which ensures the key material will be zeroized when the `ByteQueue`

's destructor runs. A sample from validate2.cpp is shown below.

ByteQueue queue; priv.AccessKey().SavePrecomputation(queue);

Where the library uses `AccessKey`

and then calls `SavePrecomputation`

, we already have the key (so there's no need to call `AccessKey`

), and we are only interested in saving the key itself (rather than the precomputation and other trap-door goodies). In keeping with the library, our function to save a public key is as follows (a private key is similar).

void SavePublicKey(const string& filename, const PublicKey& key) { ByteQueue queue; key.Save(queue); Save(filename, queue); }

`SavePublicKey`

and `SavePrivateKey`

take a Crypto++ `PublicKey`

and `PrivateKey`

and simply write the key to file. In the spirit of object orientedness, `SavePublicKey`

and `SavePrivateKey`

call `Save`

. cryptopp-key-gen's use of `Save`

is nearly identical to the library's use of `Save`

(nearly identical because object oriented interface programming has a lot of function calls that don't actually do much of anything).

If we departed a bit from object oriented and interface programming (by yanking cryptopp-key-gen `Save`

), we could probably do away with the calls to `CopyTo`

and `MessageEnd`

at the cost of duplicating functionality (heaven forbid!).

void Save(const string& filename, const BufferedTransformation& bt) { FileSink file(filename.c_str()); bt.CopyTo(file); file.MessageEnd(); }

### Loading Keys

Though we would like to leave key loading as an exercise to the reader, its probably best if an article which discusses how to load a key were to demonstrate how to do it.

Just as both `PKCS8PrivateKey`

and `X509PublicKey`

offered a `Save`

, each offers `Load`

. To use `Load`

, we create three thin wrappers (two of which are shown) that are the antithesis of the functions we used for `Save`

. That is, data will flow from a `FileSource`

, into a `BufferedTransformation`

, and then loaded by the key.

void LoadPublicKey(const string& filename, PublicKey& key); void Load(const string& filename, BufferedTransformation& bt)

cryptopp-key-gen's low level `Load`

fills the caller's `BufferedTransformation`

with the file's data:

void Load(const string& filename, BufferedTransformation& bt) { FileSource file(filename.c_str(), true /*pumpAll*/); file.TransferTo(bt); bt.MessageEnd(); }

And `LoadPublicKey`

calls load on the Crypto++ key:

void LoadPublicKey(const string& filename, PublicKey& key) { ByteQueue queue; Load(filename, queue); key.Load(queue); }

Finally, we can verify that the key "round trips" with the following.

RSA::PrivateKey key1, key2; key1.GenerateRandomWithKeySize(prng, 3072); SavePrivateKey("rsa-roundtrip.key", key1); LoadPrivateKey("rsa-roundtrip.key", key2); if(key1.GetModulus() != key2.GetModulus() || key1.GetPublicExponent() != key2.GetPublicExponent() || key1.GetPrivateExponent() != key2.GetPrivateExponent()) { throw runtime_error("key data did not round trip"); }

### Validating Keys

A key should be validated once loaded, regardless of the library that generated it or the intended purpose since you don't know who generated the key, the purpose of the key (which could be malicious), or the tool used (which could have a non-trivial flaw). **Never** apply a secret to an unvalidated key since the key may have a flaw that allows the secret's recovery. **Never** consume recovered data based on an unvalidated key (this includes authenticity of digital signatures). These warnings are concerned with 'bad' use cases (for example, dishonest participants); and not the intended 'good' use cases (where folks play fairly with others).

`CryptoMaterial`

, from which `PublicKey`

and `PrivateKey`

inherit, offers `Validate`

. `Validate`

takes a `RandomNumberGenerator`

and a level, and returns a boolean. The comments accompanying `Validate`

in `CryptoMaterial`

state (see the CryptoMaterial Class Reference):

Level denotes the level of thoroughness: 0 - using this object won't cause a crash or exception (rng is ignored); 1 - this object will probably function (encrypt, sign, etc.) correctly (but may not check for weak keys and such); 2 - make sure this object will function correctly, and do reasonable security checks; 3 - do checks that may take a long time.

In practice, you should require at least level two validation, and prefer level three validation. Level three is preferred because some applications cannot tolerate the extended processing. Level three validation may be required in some cases; for example, if the data is highly sensitive or the operational decision (following the decryption or verification) is critical. The code to validate a key once generated or loaded is as follows.

if(!rsaPrivate.Validate(prng, 3)) throw runtime_error("Rsa private key validation failed"); if(!dsaPrivate.Validate(prng, 3)) throw runtime_error("Dsa private key validation failed");

### GnuPG ElGamal Keys

GnuPG generated ElGamal keys will fail validation at level two and higher. This is because GnuPG ElGamal keys use Lim-Lee primes, and not strong or safe primes. GnuPG's documentation on ElGamal and its keys is available at *Prime-Number-Generator Subsystem Architecture*. The paper that discusses the problem that motivated GnuPG and its use of Lim-Lee primes is *A Key Recovery Attack on Discrete Log-based Schemes Using a Prime Order Subgroup* by Chae Hoon Lim and Pil Joong Lee.

To validate the GnuPG ElGamal key, you should obtain the unique factorization of p (from Section 4 of Lim and Lee's paper: [math]\displaystyle{ p = q p_1 p_2 p_3 ... p_n) }[/math]. Then ensure each [math]\displaystyle{ p_i }[/math] is prime and [math]\displaystyle{ |p_i| \gt l }[/math]. [math]\displaystyle{ l }[/math] is approximately the size of [math]\displaystyle{ q }[/math], and [math]\displaystyle{ q }[/math] is chosen due to Schnorr's subgroup attacks. To obtain the unique factorization, you can get it from the peer or factor it. According to Werner Kock of GnuPG, the unique factorization was available in secring.gpg up to (and including) version 1.4.1. After version 1.4.1, you will have to factor the key. (Note: According to GnuPG's Release Notes, version 1.4.1 was superseded in July, 2005).

If you find you need to use GnuPG and its ElGamal schemes, it is probably easiest to generate the keys using Crypto++ (since the key will validate in Crypto++ and other libraries), and then import the key into GnuPG for use.

## BER and DER Encoding

Recall from Private Key Format and Public Key Format that PKCS #8 and X.509 have an 'outer' *PrivateKeyInfo* or *SubjectPublicKeyInfo*. The outer structure includes information such as OID and version. Within the ASN.1 structure is the actual key material upon which an algorithm operates. For example, a *RsaPublicKey* would have a public exponent 'e' and modulus 'n', while a *DsaPrivateKey* key would have 'p', 'q', 'g', and 'x'.

Crypto++ offers functions to save and load the 'inner' key material via `DEREncodePrivateKey`

, `DEREncodePublicKey`

, `BERDecodePrivateKey`

, and `BERDecodePublicKey`

. When using the encoding function, keep in mind that you might lose interoperability with other libraries.

Writing a key uses DER encoding (most strict encoding rules), while reading a key uses BER decoding (sloppy decoding rules). The strict/sloppy in reading and writing is required by the ITU and promotes interoperability, and its in spirit with Postel's Law. Its the reason keys is saved using DER encoding rules, and loaded using BER encoding rules.

The code below is available in cryptopp-key-encode.zip.

### DER Encoding

The code below demonstrates saving the key material of a RSA public key. Note that we must specifically use `RSA::PrivateKey`

and `RSA::PublicKey`

.

int main(int argc, char** argv) { AutoSeededRandomPool prng; try { RSA::PrivateKey rsaPrivate; rsaPrivate.GenerateRandomWithKeySize(prng, 3072); RSA::PublicKey rsaPublic(rsaPrivate); EncodePrivateKey("rsa-private.key", rsaPrivate); EncodePublicKey("rsa-public.key", rsaPublic); cout << "Successfully generated and saved RSA keys" << endl; } catch(CryptoPP::Exception& e) { cerr << e.what() << endl; return -1; } return 0; } void EncodePrivateKey(const string& filename, const RSA::PrivateKey& key) { ByteQueue queue; key.DEREncodePrivateKey(queue); Encode(filename, queue); } void EncodePublicKey(const string& filename, const RSA::PublicKey& key) { ByteQueue queue; key.DEREncodePublicKey(queue); Encode(filename, queue); } void Encode(const string& filename, const BufferedTransformation& bt) { FileSink file(filename.c_str()); bt.CopyTo(file); file.MessageEnd(); }

### BER Decoding

BER Decoding is somewhat more cumbersome to use. The generic interface of `DEREncodePrivateKey`

and `DEREncodePublicKey`

are:

BERDecodePrivateKey (BufferedTransformation &bt, bool parametersPresent, size_t size) BERDecodePublicKey (BufferedTransformation &bt, bool parametersPresent, size_t size)

Examining the source code in `rsa.cpp`

, neither `parametersPresent`

nor `size`

are used (`parametersPresent`

is only needed for `DL_PrivateKey_EC<EC>`

, which is a base class for an elliptic curve key), so we pass false. Though unused, we pass the number of bytes in the queue for `size`

.

int main(int argc, char** argv) { AutoSeededRandomPool prng; try { RSA::PrivateKey k1; DecodePrivateKey("rsa-private.key", k1); RSA::PublicKey k2; DecodePublicKey("rsa-public.key", k2); cout << "Successfully loaded RSA keys" << endl; //////////////////////////////////////////////////////////////////////////////////// if(!k1.Validate(prng, 3)) throw runtime_error("Rsa private key validation failed"); if(!k2.Validate(prng, 3)) throw runtime_error("Rsa public key validation failed"); cout << "Successfully validated RSA keys" << endl; //////////////////////////////////////////////////////////////////////////////////// if(k1.GetModulus() != k2.GetModulus() || k1.GetPublicExponent() != k2.GetPublicExponent()) { throw runtime_error("key data did not round trip"); } cout << "Successfully round-tripped RSA keys" << endl; } catch(CryptoPP::Exception& e) { cerr << e.what() << endl; return -1; } return 0; } void DecodePrivateKey(const string& filename, RSA::PrivateKey& key) { ByteQueue queue; Decode(filename, queue); key.BERDecodePrivateKey(queue, false /*paramsPresent*/, queue.MaxRetrievable()); } void DecodePublicKey(const string& filename, RSA::PublicKey& key) { ByteQueue queue; Decode(filename, queue); key.BERDecodePublicKey(queue, false /*paramsPresent*/, queue.MaxRetrievable()); } void Decode(const string& filename, BufferedTransformation& bt) { FileSource file(filename.c_str(), true /*pumpAll*/); file.TransferTo(bt); bt.MessageEnd(); }

## Hex and Base64 Encoding

A key that is written in binary format is sometimes referred to as *raw* or *uncooked* (this applies to data in general, and not keys in particular). Sometimes it is advantageous (or required) to add some additional processing - for example, hex encoding or base64 encoding. A raw key that has been subjected to additional processing is sometimes referred to as *cooked* or *baked*.

In the accompanying image, anyone who has spent time examining dumps will recognize the binary string `0x30 0x82 ...`

probably indicates the file is raw ASN.1. Similarly, a person will recognize a file starting with the ASCII string `MII...`

is probably a Base64 encoded ASN.1 file.

Below, Hexadecimal and Base64 encoding are shown. The decoding is left as an exercise to the reader.

### Hex Encoding and Decoding

If we wanted to hex encode the key during a save, we would add the following signatures.

void SaveHexPrivateKey(const string& filename, const PrivateKey& key); void SaveHexPublicKey(const string& filename, const PublicKey& key); void SaveHex(const string& filename, const BufferedTransformation& bt);

Finally, the hex encoded implementations would be as follows.

void SaveHexPrivateKey(const string& filename, const PrivateKey& key) { ByteQueue queue; key.Save(queue); SaveHex(filename, queue); } void SaveHexPublicKey(const string& filename, const PublicKey& key) { ByteQueue queue; key.Save(queue); SaveHex(filename, queue); } void SaveHex(const string& filename, const BufferedTransformation& bt) { HexEncoder encoder; bt.CopyTo(encoder); encoder.MessageEnd(); Save(filename, encoder); }

### Base64 Encoding and Decoding

If we wanted to add Base64 processing to the key during a save, we would add the following signatures.

void SaveBase64PrivateKey(const string& filename, const PrivateKey& key); void SaveBase64PublicKey(const string& filename, const PublicKey& key); void SaveBase64(const string& filename, const BufferedTransformation& bt);

Finally, the base 64'd implementations would be as follows.

void SaveBase64PrivateKey(const string& filename, const PrivateKey& key) { ByteQueue queue; key.Save(queue); SaveBase64(filename, queue); } void SaveBase64PublicKey(const string& filename, const PublicKey& key) { ByteQueue queue; key.Save(queue); SaveBase64(filename, queue); } void SaveBase64(const string& filename, const BufferedTransformation& bt) { Base64Encoder encoder; bt.CopyTo(encoder); encoder.MessageEnd(); Save(filename, encoder); }

## PEM Encoded Keys

Crypto++ does not convert to/from PEM encoding. You have two choices if you need to read and write PEM encoded keys. First, you can add the PEM Pack to the library and recompile it. Second, you can use the following to convert from a PEM encoded key to plain BER/DER encoded key, and then use `BERDecodePrivateKey`

to load the encoded key into a `PubliceKey`

or `PrivateKey`

structure.

string RSA_PRIV_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" "MIIBOgIBAAJBAK8Q+ToR4tWGshaKYRHKJ3ZmMUF6jjwCS/u1A8v1tFbQiVpBlxYB\n" "paNcT2ENEXBGdmWqr8VwSl0NBIKyq4p0rhsCAQMCQHS1+3wL7I5ZzA8G62Exb6RE\n" "INZRtCgBh/0jV91OeDnfQUc07SE6vs31J8m7qw/rxeB3E9h6oGi9IVRebVO+9zsC\n" "IQDWb//KAzrSOo0P0yktnY57UF9Q3Y26rulWI6LqpsxZDwIhAND/cmlg7rUz34Pf\n" "SmM61lJEmMEjKp8RB/xgghzmCeI1AiEAjvVVMVd8jCcItTdwyRO0UjWU4JOz0cnw\n" "5BfB8cSIO18CIQCLVPbw60nOIpUClNxCJzmMLbsrbMcUtgVS6wFomVvsIwIhAK+A\n" "YqT6WwsMW2On5l9di+RPzhDT1QdGyTI5eFNS+GxY\n" "-----END RSA PRIVATE KEY-----"; static string HEADER = "-----BEGIN RSA PRIVATE KEY-----"; static string FOOTER = "-----END RSA PRIVATE KEY-----"; size_t pos1, pos2; pos1 = RSA_PRIV_KEY.find(HEADER); if(pos1 == string::npos) throw runtime_error("PEM header not found"); pos2 = RSA_PRIV_KEY.find(FOOTER, pos1+1); if(pos2 == string::npos) throw runtime_error("PEM footer not found"); // Start position and length pos1 = pos1 + HEADER.length(); pos2 = pos2 - pos1; string keystr = RSA_PRIV_KEY.substr(pos1, pos2); // Base64 decode, place in a ByteQueue ByteQueue queue; Base64Decoder decoder; decoder.Attach(new Redirector(queue)); decoder.Put((const byte*)keystr.data(), keystr.length()); decoder.MessageEnd(); // Write to file for inspection FileSink fs("decoded-key.der"); queue.CopyTo(fs); fs.MessageEnd(); try { CryptoPP::RSA::PrivateKey rsaPrivate; rsaPrivate.BERDecodePrivateKey(queue, false /*paramsPresent*/, queue.MaxRetrievable()); // BERDecodePrivateKey is a void function. Here's the only check // we have regarding the DER bytes consumed. ASSERT(queue.IsEmpty()); AutoSeededRandomPool prng; bool valid = rsaPrivate.Validate(prng, 3); if(!valid) cerr << "RSA private key is not valid" << endl; cout << "N:" << rsaPrivate.GetModulus() << endl; cout << "E:" << rsaPrivate.GetPublicExponent() << endl; cout << "D:" << rsaPrivate.GetPrivateExponent() << endl; } catch (const Exception& ex) { cerr << ex.what() << endl; exit (1); }

If the key is password protected, then use one of the OpenSSL commands listed below to remove the password and convert to PKCS #8. For example:

openssl pkcs8 -nocrypt -in rsa-key.pem -inform PEM -topk8 -outform DER -out rsa-key.der

## High Level Objects

It might be prudent to use Crypto++'s higher level objects, such as `RSAES_PKCS1v15_Decryptor`

, `RSAES_OAEP_SHA_Decryptor`

, and `RSASSA_PKCS1v15_SHA_Signer`

to generate key pairs, rather than `RSA::PrivateKey`

. This is because the higher level objects will have knowledge of (and corresponding logic for) domain specific issues, such as use of *safe primes* versus *random primes*. When in doubt, use the high level objects.

### Decryptors and Signers

Generally speaking, Crypto++ high level objects such as `RSAES_OAEP_SHA_Decryptor`

and `RSASSA_PKCS1v15_SHA_Signer`

"HAVE A" *PrivateKey*. More correctly, decryptors inherit from `PK_Decryptor`

and signers inherit from `PK_Signer`

, both of which trace their lineage back to `PrivateKeyAlgorithm`

. See PK_Decryptor Class Reference, PK_Signer Class Reference, and PrivateKeyAlgorithm Class Reference.

### Encryptors and Verifiers

Similar to decryptors and signers, Crypto++ high level objects such as `RSAES_OAEP_SHA_Encryptor`

and `RSASSA_PKCS1v15_SHA_Verifier`

"HAVE A" *PublicKey*. Encryptors inherit from `PK_Encryptor`

and verifiers inherit from `PK_Verifier`

, both of which trace their lineage back to `PublicKeyAlgorithm`

. See PK_Encryptor Class Reference, PK_Verifier Class Reference, and PublicKeyAlgorithm Class Reference.

### RSAES_OAEP_SHA_{Encryptor|Decryptor}

The following demonstrates how to create a public and private key pair using `RSAES_OAEP_SHA_Decryptor`

and `RSAES_OAEP_SHA_Encryptor`

. Since the high level objects use composition ("HAS A"), we use `AccessKey`

to fetch the key before calling methods on the key.

RSAES_OAEP_SHA_Decryptor rsaPrivate; rsaPrivate.AccessKey().GenerateRandomWithKeySize(prng, 3072); RSAES_OAEP_SHA_Encryptor rsaPublic(rsaPrivate); SavePrivateKey("rsa-private.key", rsaPrivate.AccessKey()); SavePublicKey("rsa-public.key", rsaPublic.AccessKey());

### RSASSA_PKCS1v15_SHA_{Signer|Verifier}

The following shows how to create a public and private key pair using `RSASSA_PKCS1v15_SHA_Signer`

and `RSASSA_PKCS1v15_SHA_Verifier`

. The objects also use composition ("HAS A"), so `AccessKey`

fetches the key before calling methods on the key.

RSASSA_PKCS1v15_SHA_Signer rsaPrivate; rsaPrivate.AccessKey().GenerateRandomWithKeySize(prng, 3072); RSASSA_PKCS1v15_SHA_Verifier rsaPublic(rsaPrivate); SavePrivateKey("rsa-private.key", rsaPrivate.AccessKey()); SavePublicKey("rsa-public.key", rsaPublic.AccessKey());

## Dumping PKCS #8 and X.509 Keys

Now that we have an understanding of the key formats, its time to poke and prod the keys a bit to further our understanding. To dump the keys, Peter Guttman's *dumpasn1* works well from the command line. For the visual world, Objective Systems has a free ASN.1 Viewer.

### RSA PublicKey

Recall that cryptopp-key-gen saved rsa-public.key as a X.509 *PublicKeyInfo* type. In programming terms, here's what the *PublicKeyInfo* looks like (the definitions are taken directly from X.509).

PublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, PublicKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm ALGORITHM.id, parameters ALGORITHM.type OPTIONAL }

X.509 specifies the format includes an inner *PublicKey*, but does not specify what the inner key is. Since the OID is for and RSA public key, the type is actually a *RSAPublicKey*. In this case, we can get the definition from X.509 or get the definition from PKCS #1 (the definitions are equivalent):

RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER }

Often times, the public exponent can act as a library indicator if the default is used: Crypto++ uses 17, GnuTLS, Java, and OpenSSL use 65537, and Microsoft's .Net uses 3. Notice the public exponent is selected with a low Hamming weight to speed up computation.

Above is the screen capture of a rsa-public.key dump. *PublicKeyInfo* starts with a sequence at octet 0 and is 221 octets in length. A nested sequence starts at offset 3, which implies the first sequence is 3 octets. The second sequence houses an OID: 1.2.840.113549.1.1.1, which is a RSA key and an ASN.1 NULL (the ALGORITHM.type).

At offset 18 (with a length of 203 octets), the *RSAPublicKey* begins. From above we have the public modulus or *n* which is `00 F8 54 F4 ... D9 D2 15 CC ... DB 7C 5F 2D`

(*dumpasn* takes the *-a* options which shows all the data, and not just the first 128 octets of an ASN.1 type). Following *n* is the public exponent.

OID lookups can usually be performed at the OID Repository if the tool cannot resolve an OID. Sometimes a web search is required to find a bleeding edge RFC or other standard's amendment.

### RSA PrivateKey

Dumping an ASN.1 encoded private key is similar to dumping the public key. The format is a *PrivateKeyInfo* (rather than *PublicKeyInfo*). Its structure is as follows and includes a version and optional attributes.

PrivateKeyInfo ::= SEQUENCE { version Version, privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, privateKey PrivateKey, attributes [0] IMPLICIT Attributes OPTIONAL }

Things don't get interesting until we look at the *PrivateKey* encapsulated byt the *PrivateKeyInfo*. As with the *PublicKey*, since the *PrivateKey* has a RSA OID in the *PrivateKeyInfo*, the type is *RSAPrivateKey*.

Below is the structure which includes the familiar suspects: *e*, *d*, *p*, and *q*. The exponents are used for precomputation so the holder of the key does not need to use the *e*, *d*, *n* and the Chinese Remainder Theorem to recalculate useful internal values.

RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, publicExponent INTEGER, privateExponent INTEGER, prime1 INTEGER, prime2 INTEGER, exponent1 INTEGER, exponent2 INTEGER, coefficient INTEGER }

### DSA PublicKey

The steps to dump a DSA public key are identical to that of RSA. DSA's OID is 1.2.840.10040.4.1. DSA will include DSS domain parameters in the optional parameters.

PublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, PublicKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm ALGORITHM.id, parameters Dss-Parms } Dss-Parms ::= SEQUENCE { p INTEGER, q INTEGER, g INTEGER }

The DSA public key is simply the public exponent *Y* encoded as a bit string:

DSAPublicKey ::= BITSTRING { publicExponent INTEGER }

### DSA PrivateKey

A DSA private key also uses the 1.2.840.10040.4.1 OID. Where a public key used a bit string (to encode its public exponent), the private key uses an octet string to encode the private exponent *X*:

PrivateKeyInfo ::= SEQUENCE { version Version, algorithm AlgorithmIdentifier, PrivateKey OCTETSTRING } AlgorithmIdentifier ::= SEQUENCE { algorithm ALGORITHM.id, parameters Dss-Parms } Dss-Parms ::= SEQUENCE { p INTEGER, q INTEGER, g INTEGER } DSAPrivateKey ::= OCTETSTRING { privateExponent INTEGER }

## OpenSSL Commands

The following offers OpenSSL commands for those who need to inter-operate with the OpenSSL library.

By default, Crypto++ uses DER encoding, while OpenSSL uses Privacy Enhanced Mail (PEM) encoding. PEM encoding is a Base64 encoding with a header and footer. One usually moves between DER and PEM encodings with the `-inform`

and `-outform`

switches.

$ openssl genrsa -out rsa-openssl.pem 256 Generating RSA private key, 256 bit long modulus ... $ cat rsa-openssl.pem -----BEGIN RSA PRIVATE KEY----- MIGrAgEAAiEA1+FPfSd+EaXYYU0LlPZ0K+k3uWFUb+s/8NxV9PEl1psCAwEAAQIg RjBfY9W/S4WcgKZIKbqnsjYuQO5nEAGAjx/XY9za48ECEQD2EpdAXBwXDBC9NQGD yUAJAhEA4JblIvDA6WOWr4KO79oCgwIQUfoFi3pkHUV2uiHTfFzqSQIRAJOcWamn VwOOgwGD6/JU9YUCEQDUVTl+0TNEiA3SbsvbRya7 -----END RSA PRIVATE KEY-----

`genpkey(1)`

can also be used.

### RSA Public Key (*SubjectPublicKeyInfo*)

The following commands create a RSA public key in PKCS #1/X.509 format which Crypto++ can consume. Recall the format is *SubjectPublicKeyInfo* with an OID (1.2.840.113549.1.1.1), and the *RSAPublicKey* (the inner key material).

$ openssl genrsa -out rsa-openssl.pem 3072 $ openssl rsa -in rsa-openssl.pem -pubout -outform DER -out rsa-openssl.der

### RSA Private Key (*PrivateKeyInfo*)

The following commands create an unencrypted RSA private key in PKCS #8 format which Crypto++ can consume. Recall the format is *PrivateKeyInfo* with an OID (1.2.840.113549.1.1.1), version, and the *RSAPrivateKey* (the inner key material).

$ openssl genrsa -out rsa-openssl.pem 3072 $ openssl pkcs8 -nocrypt -in rsa-openssl.pem -inform PEM -topk8 -outform DER -out rsa-openssl.der

### DSA Public Key (*SubjectPublicKeyInfo*)

The following commands create a DSA public key in PKCS #1/X.509 format which Crypto++ can consume. Recall the format is *SubjectPublicKeyInfo* with an OID (1.2.840.10040.4.1), DSS parameters, and the *DSAPublicKey* (the inner key material).

$ openssl dsaparam -out dsa-param-openssl.pem 1024 $ openssl gendsa -out dsa-openssl.pem dsa-param-openssl.pem $ openssl dsa -in dsa-openssl.pem -pubout -outform DER -out dsa-openssl.der

### DSA Private Key (*PrivateKeyInfo*)

The following commands create an unencrypted DSA private key in PKCS #8 format which Crypto++ can consume. Recall the format is *PrivateKeyInfo* with an OID (1.2.840.10040.4.1), version, DSS parameters, and the *DSAPrivateKey* (the inner key material).

$ openssl dsaparam -out dsa-param-openssl.pem 1024 $ openssl gendsa -out dsa-openssl.pem dsa-param-openssl.pem $ openssl pkcs8 -nocrypt -in dsa-openssl.pem -inform PEM -topk8 -outform DER -out dsa-openssl.der

### ECC Public Key (*SubjectPublicKeyInfo*)

The following commands create a ECC public key in PKCS #1/X.509 format which Crypto++ can consume. The curve is SEC's prime 256.

$ openssl ecparam -param_enc explicit -name secp256k1 -genkey -outform PEM -out ec-openssl.pem $ openssl ec -param_enc explicit -inform PEM -in ec-openssl.pem -pubout -outform DER -out ec-openssl.der

### ECC Private Key (*PrivateKeyInfo*)

The following command creates an unencrypted ECC private key in PKCS #8 format which Crypto++ can consume. The curve is SEC's prime 256.

$ openssl ecparam -name secp256k1 -genkey -param_enc explicit -outform DER -out ec-openssl.der

## GnuTLS Commands

The following GnuTLS commands should help those who use Gnu's secure transport library, GnuTLS.

$ gnutls-cli --version gnutls-cli (GnuTLS) 2.8.5 Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. ... Written by Nikos Mavrogiannopoulos.

### RSA Public Key (*SubjectPublicKeyInfo*)

According to Nikos Mavrogiannopoulos, GnuTLS 2.11 and above can use `certtool`

's `--certificate-pubkey`

switch. See *How to Create/Derive DER Encoded Public Keys (PKCS #1/X.509 SubjectPublicKeyInfo)?*.

### RSA Private Key (*PrivateKeyInfo*)

$ certtool --generate-privkey --pkcs8 --outder --bits 3072 --outfile rsa-gnutls.der

### DSA Public Key (*SubjectPublicKeyInfo*)

According to Nikos Mavrogiannopoulos, GnuTLS 2.11 and above can use `certtool`

's `--certificate-pubkey`

switch. See *How to Create/Derive DER Encoded Public Keys (PKCS #1/X.509 SubjectPublicKeyInfo)?*.

### DSA Private Key (*PrivateKeyInfo*)

$ certtool --dsa --generate-privkey --pkcs8 --outder --bits 1024 --outfile dsa-gnutls.der

## Downloads

cryptopp-key-gen.zip - Generates, saves, loads, and validates keys for RSA and DSA in PKCS #8 and X.509 format.

cryptopp-key-encode.zip - Generates, encodes, and decodes RSA keys using DER encoding and BER decoding.