diff options
Diffstat (limited to 'doc/pubkey.txt')
-rw-r--r-- | doc/pubkey.txt | 365 |
1 files changed, 222 insertions, 143 deletions
diff --git a/doc/pubkey.txt b/doc/pubkey.txt index 2b765cc10..1be471e1b 100644 --- a/doc/pubkey.txt +++ b/doc/pubkey.txt @@ -4,80 +4,240 @@ Public Key Cryptography ================================= -Quick Start ---------------------------------- +Public key cryptography (also called assymmetric cryptography) is a +collection of techniques allowing for encryption, signatures, and key +agreement. -Let's create a 2048-bit RSA private key, serialize the public key as a -PKCS #1 file with PEM encoding (which can be understood by many other -cryptographic programs), and then load it on another machine:: +Key Objects +---------------------------------------- - // everyone does: - AutoSeeded_RNG rng; +Public and private keys are represented by classes ``Public_Key`` and +it's subclass ``Private_Key``. The use of inheritence here means that +a ``Private_Key`` can be converted into a reference to a public key. - // Alice - RSA_PrivateKey priv_rsa(rng, 2048 /* bits */); +None of the functions on ``Public_Key`` and ``Private_Key`` itself are +particularly useful for users of the library, because 'bare' public key +operations are *very insecure*. The only purpose of these functions is +to provide a clean interface that higher level operations can be built +on. So really the only thing you need to know is that when a function +takes a reference to a ``Public_Key``, it can take any public key or +private key, and similiarly for ``Private_Key``. - std::string alice_pem = X509::PEM_encode(priv_rsa); +Types of ``Public_Key`` include ``RSA_PublicKey``, ``DSA_PublicKey``, +``ECDSA_PublicKey``, ``DH_PublicKey``, ``ECDH_PublicKey``, +``RW_PublicKey``, ``NR_PublicKey``,, and ``GOST_3410_PublicKey``. +There are cooresponding ``Private_Key`` classes for each of these +algorithms. - // send alice_pem to Bob, who does +.. _creating_new_private_keys: - // Bob - std::auto_ptr<Public_Key> alice(load_key(alice_pem)); +Creating New Private Keys +---------------------------------------- - RSA_PublicKey* alice_rsa = dynamic_cast<RSA_PublicKey>(alice); - if(alice_rsa) - { - /* ... */ - } +Creating a new private key requires two things: a source of +random numbers (see :ref:`random_number_generators`) and some +algorithm specific parameters that define the *security level* +of the resulting key. For instance, the security level of an RSA +key is (at least in part) defined by the length of the public key +modulus in bits. So to create a new RSA private key, you would call -Creating New Public Key Pairs ---------------------------------- +.. cpp:function:: RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng, size_t bits) + + A constructor that creates a new random RSA private key with a modulus + of length *bits*. + +Algorithms based on the discrete-logarithm problem uses what is called +a *group*; a group can safely be used with many keys, and for some +operations, like key agreement, the two keys *must* use the same +group. There are currently two kinds of discrete logarithm groups +supported in botan: the integers modulo a prime, represented by +:ref:`dl_group`, and elliptic curves in GF(p), represented by +:ref:`ec_dompar`. A rough generalization is that the larger the group +is, the more secure the algorithm is, but coorespondingly the slower +the operations will be. + +Given a ``DL_Group``, you can create new DSA, Diffie-Hellman, and +Nyberg-Rueppel key pairs with + +.. cpp:function:: DSA_PrivateKey::DSA_PrivateKey(RandomNumberGenerator& rng, const DL_Group& group, const BigInt& x = 0) + +.. cpp:function:: DH_PrivateKey::DH_PrivateKey(RandomNumberGenerator& rng, const DL_Group& group, const BigInt& x = 0) + +.. cpp:function:: NR_PrivateKey::NR_PrivateKey(RandomNumberGenerator& rng, const DL_Group& group, const BigInt& x = 0) + +.. cpp:function:: ElGamal_PrivateKey::ElGamal_PrivateKey(RandomNumberGenerator& rng, const DL_Group& group, const BigInt& x = 0) + + The optional *x* parameter to each of these contructors is a private + key value. This allows you to create keys where the private key is + formed by some special technique; for instance you can use the hash + of a password (see :ref:`pbkdf` for how to do that) as a private key + value. Normally, you would leave the value as zero, letting the + class generate a new random key. + +Finally, given an ``EC_Domain_Params`` object, you can create a new +ECDSA, ECDH, or GOST 34.10 private key with + +.. cpp:function:: ECDSA_PrivateKey::ECDSA_PrivateKey(RandomNumberGenerator& rng, const EC_Domain_Params& domain) + +.. cpp:function:: ECDH_PrivateKey::ECDH_PrivateKey(RandomNumberGenerator& rng, const EC_Domain_Params& domain) + +.. cpp:function:: GOST_3410_PrivateKey::GOST_3410_PrivateKey(RandomNumberGenerator& rng, const EC_Domain_Params& domain) + +Unlike the integer modulo a prime key types, the constructor that takes a +predefined ``BigInt`` private key value is different: + +.. cpp:function:: ECDSA_PrivateKey::ECDSA_PrivateKey(const EC_Domain_Params& domain, const BigInt& x) + +.. cpp:function:: ECDH_PrivateKey::ECDH_PrivateKey(const EC_Domain_Params& domain, const BigInt& x) + +.. cpp:function:: GOST_3410_PrivateKey::GOST_3410_PrivateKey(const EC_Domain_Params& domain, const BigInt& x) + +.. note:: + + It is likely that these constructors will be removed in a future + release, and a third optional parameter will be added to the + constructors described above, to match the integer modulo prime + versions. Only use them if you really need them. + + +Serializing Private Keys +---------------------------------------- + +The standard format for serializing a private key is PKCS #8, the +operations for which are defined in ``pkcs8.h``. It supports both +unencrypted and encrypted storage. + +.. cpp:function:: SecureVector<byte> PKCS8::BER_encode(const Private_Key& key, RandomNumberGenerator& rng, const std::string& password, const std::string& pbe_algo = "") + + Takes any private key object, serializes it, encrypts it using + *password*, and returns a binary structure representing the private + key. + + The final (optional) argument, *pbe_algo*, specifies a particular + password based encryption (or PBE) algorithm. If you don't specify a + PBE, a sensible default will be used. + +.. cpp:function:: std::string PKCS8::PEM_encode(const Private_Key& key, RandomNumberGenerator& rng, const std::string& pass, const std::string& pbe_algo = "") + + This formats the key in the same manner as ``BER_encode``, but + additionally encodes it into a text format with identifying + headers. Using PEM encoding is *highly* recommended for many + reasons, including compatibility with other software, for + transmission over 8-bit unclean channels, because it can be + identified by a human without special tools, and because it + sometimes allows more sane behavior of tools that process the data. + +Unencrypted serialization is also supported. + +.. warning:: + + In most situations, using unecrypted private key storage is a bad + idea, because anyone can come along and grab the private key without + having to know any passwords or other secrets. Unless you have very + particular security requirements, always use the versions that + encrypt the key based on a passphrase, described above. + +.. cpp:function:: SecureVector<byte> PKCS8::BER_encode(const Private_Key& key) + + Serializes the private key and returns the result. + +.. cpp:function:: std::string PKCS8::PEM_encode(const Private_Key& key) + + Serializes the private key, base64 encodes it, and returns the + result. + +Last but not least, there are some functions that will load (and +decrypt, if necessary) a PKCS #8 private key: + +.. cpp:function:: Private_Key* PKCS8::load_key(DataSource& in, RandomNumberGenerator& rng, const User_Interface& ui) + +.. cpp:function:: Private_Key* PKCS8::load_key(DataSource& in, RandomNumberGenerator& rng, std::string passphrase = "") + +.. cpp:function:: Private_Key* PKCS8::load_key(const std::string& filename, RandomNumberGenerator& rng, const User_Interface& ui) + +.. cpp:function:: Private_Key* PKCS8::load_key(const std::string& filename, RandomNumberGenerator& rng, const std::string& passphrase = "") + +The result is an object allocated using ``new``. + +The versions that pass the passphrase as a ``std::string`` are +primarily for compatibility, but they are useful in limited +circumstances. The ``User_Interface`` versions are how ``load_key`` is +implemented, and provides for much more flexibility. If the passphrase +passed in is not correct, then an exception is thrown and that is +that. However, if you pass in an UI object, then the UI object can +keep asking the user for the passphrase until they get it right (or +until they cancel the action, though the UI interface). A +``User_Interface`` has very little to do with talking to users; it's +just a way to glue together Botan and whatever user interface you +happen to be using. You can think of it as a user interface +interface. The default ``User_Interface`` is rather dumb, and acts +rather like the versions taking the ``std::string``; it tries the +passphrase passed in first, and then it cancels. + +.. note:: + + In a future version, it is likely that ``User_Interface`` will be + replaced by a simple callback using ``std::function``. + +.. _dl_group: -The library has interfaces for public key encryption, signatures, and -key agreement that do not require knowing the exact algorithm in -use. One place where we *do* need to know exactly what kind of -algorithm is in use is when we are creating a key. - -There are currently three kinds of public key algorithms in Botan: -ones based on integer factorization (RSA and Rabin-Williams), ones -based on the discrete logarithm problem in the integers modulo a prime -(DSA, Diffie-Hellman, Nyberg-Rueppel, and ElGamal), and ones based on -the discrete logarithm problem in an elliptic curve (ECDSA, ECDH, and -GOST 34.10). The systems based on discrete logarithms (in either -regular integers or elliptic curves) use a group (a mathematical -term), which can be shared among many keys. An elliptic curve group is -represented by the class ``EC_Domain_Params``, while a modulo-prime -group is represented by a ``DL_Group``. - -There are two ways to create a DL private key (such as -``DSA_PrivateKey``). One is to pass in just a ``DL_Group`` object -- a -new key will automatically be generated. The other involves passing in -a group to use, along with both the public and private values (private -value first). - -Since in integer factorization algorithms, the modulus used isn't -shared by other keys, we don't use this notion. You can create a new -key by passing in a ``size_t`` telling how long (in bits) the key -should be, or you can copy an pre-existing key by passing in the -appropriate parameters (primes, exponents, etc). For RSA and -Rabin-Williams (the two IF schemes in Botan), the parameters are all -``BigInt``: prime 1, prime 2, encryption exponent, decryption -exponent, modulus. The last two are optional, since they can easily be -derived from the first three. - -Creating a DL_Group +DL_Group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are quite a few ways to get a ``DL_Group`` object. The best is -to use the function ``get_dl_group``, which takes a string naming a -group; it will either return that group, if it knows about it, or -throw an exception. Names it knows about include "modp/ietf/N" where N -is 768, 1024, 1536, 2048, 3072, 4096, 6144, and 8192 for -Diffie-Hellman and ElGamal. DSA-style groups are named "dsa/jce/N" for -N 512, 768, 1024, and "dsa/botan/N" for 2048 and 3072. +As described in :ref:`creating_new_private_keys`, a discrete logarithm +group can be shared among many keys, even keys created by users who do +not trust each other. However, it is necessary to trust the entity who +created the group; that is why organization like NIST use algorithms +which generate groups in a deterministic way such that creating a +bogus group would require breaking some trusted cryptographic +primitive like SHA-2. + +Instantiating a ``DL_Group`` simply requires calling + +.. cpp:function:: DL_Group::DL_Group(const std::string& name) + + The *name* parameter is a specially formatted string that consists + of three things, the type of the group ("modp" or "dsa"), the + creator of the group, and the size of the group in bits, all + delimited by '/' characters. + + Currently all "modp" groups included in botan are ones defined by + the Internet Engineering Task Force, so the provider is "ietf", and + the strings look like "modp/ietf/N" where N can be any of 768, 1024, + 1536, 2048, 3072, 4096, 6144, or 8192. This group type is used + for Diffie-Hellman and ElGamal algorithms. + + The other type, "dsa" is used for DSA and Nyberg-Rueppel keys. They + can also be used with Diffie-Hellman and ElGamal, but this is less + common. The currently available groups are "dsa/jce/N" for N in 512, + 768, or 1024, and "dsa/botan/N" with N being 2048 or 3072. The + "jce" groups are the standard DSA groups used in the Java + Cryptography Extensions, while the "botan" groups were randomly + generated using the FIPS 186-3 algorithm by the library maintainers. + +You can generate a new random group using + +.. cpp:function:: DL_Group::DL_Group(RandomNumberGenerator& rng, PrimeType type, size_t pbits, size_t qbits = 0) -You can also generate a new random group. This is not recommend, -because it is quite slow, especially for safe primes. + The *type* can be either ``Strong``, ``Prime_Subgroup``, or + ``DSA_Kosherizer``. *pbits* specifies the size of the prime in + bits. If the *type* is ``Prime_Subgroup`` or ``DSA_Kosherizer``, + then *qbits* specifies the size of the subgroup. + +You can export a ``DL_Group`` using + +.. cpp:function:: SecureVector<byte> DL_Group::DER_Encode(Format format) + +or + +.. cpp:function:: std::string DL_Group::PEM_encode(Format format) + +where *format* is any of + +.. _ec_dompar: + +EC_Domain_Params +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Key Checking --------------------------------- @@ -291,19 +451,9 @@ To import and export public keys, use: .. cpp:function:: MemoryVector<byte> X509::BER_encode(const Public_Key& key) - Takes any public key object, and returns a standard binary structure - representing the key which can be read by many other crypto - libraries. .. cpp:function:: std::string X509::PEM_encode(const Public_Key& key) - This formats the key the same as ``BER_encode``, but additionally - encodes it into a text format with identifying headers. Using PEM - encoding is *highly* recommended for many reasons, including - compatibility with other software, for transmission over 8-bit - unclean channels, because it can be identified by a human without - special tools, and because it sometimes allows more sane behavior of - tools that process the data. .. cpp:function:: Public_Key* X509::load_key(DataSource& in) @@ -325,74 +475,3 @@ To import and export public keys, use: Importing/Exporting Private Keys ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are two different options for private key import/export. The -first is a plaintext version of the private key. This is supported by -the following functions: - -.. cpp:function:: SecureVector<byte> PKCS8::BER_encode(const Private_Key& key) - -.. cpp:function:: std::string PKCS8::PEM_encode(const Private_Key& key) - -These functions are similiar to the X.509 functions described in -:ref:`import_export_public_keys`. The only difference is that they take -a ``Private_Key`` object instead. In most situations, using these -versions is a bad idea, because anyone can come along and grab the -private key without having to know any passwords or other -secrets. Unless you have very particular security requirements, always -use the versions that encrypt the key based on a passphrase described -below. For importing, the same functions can be used for encrypted and -unencrypted keys. - -The other way to export a PKCS #8 key is to first encode it in the -same manner as done above, then encrypt it using a passphrase, and -store the whole thing into another structure. This method is -definitely preferred, since otherwise the private key is -unprotected. The algorithms and structures used here are standardized -by PKCS #5 and PKCS #8, and can be read by many other crypto -libraries: - -.. cpp:function:: SecureVector<byte> PKCS8::BER_encode(const Private_Key& key, RandomNumberGenerator& rng, const std::string& pass, const std::string& pbe_algo = "") - -.. cpp:function:: std::string PKCS8::PEM_encode(const Private_Key& key, RandomNumberGenerator& rng, const std::string& pass, const std::string& pbe_algo = "") - - -There are three new arguments needed here to support the encryption -process. The first is a ``RandomNumberGenerator``, which is used to -generate salts to randomize the encryption. The ``pass`` argument is -the passphrase that will be used to encrypt the key. Both of these are -required. The final (optional) argument, ``pbe_algo``, specifies a -particular password based encryption (or PBE) algorithm. If you don't -specify a PBE, a sensible default will be used. - -Last but not least, there are some functions that will load (and -decrypt, if necessary) a PKCS #8 private key: - -.. cpp:function:: Private_Key* PKCS8::load_key(DataSource& in, RandomNumberGenerator& rng, const User_Interface& ui) - -.. cpp:function:: Private_Key* PKCS8::load_key(DataSource& in, RandomNumberGenerator& rng, std::string passphrase = "") - -.. cpp:function:: Private_Key* PKCS8::load_key(const std::string& filename, RandomNumberGenerator& rng, const User_Interface& ui) - -.. cpp:function:: Private_Key* PKCS8::load_key(const std::string& filename, RandomNumberGenerator& rng, const std::string& passphrase = "") - -The versions that pass the passphrase as a ``std::string`` are -primarily for compatibility, but they are useful in limited -circumstances. The ``User_Interface`` versions are how ``load_key`` is -implemented, and provides for much more flexibility. If the passphrase -passed in is not correct, then an exception is thrown and that is -that. However, if you pass in an UI object, then the UI object can -keep asking the user for the passphrase until they get it right (or -until they cancel the action, though the UI interface). A -``User_Interface`` has very little to do with talking to users; it's -just a way to glue together Botan and whatever user interface you -happen to be using. You can think of it as a user interface -interface. The default ``User_Interface`` is rather dumb, and acts -rather like the versions taking the ``std::string``; it tries the -passphrase passed in first, and then it cancels. - -All versions need access to a ``RandomNumberGenerator`` in order to -perform probabilistic tests on the loaded key material. - -After loading a key, you can use ``dynamic_cast`` to find out what -operations it supports, and use it appropriately. Remember to -``delete`` the object once you are done with it. |