]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3634a] rebased #3634
authorFrancis Dupont <fdupont@isc.org>
Sat, 23 May 2015 09:31:28 +0000 (11:31 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sat, 23 May 2015 09:31:28 +0000 (11:31 +0200)
20 files changed:
src/lib/cryptolink/Makefile.am
src/lib/cryptolink/botan_asym.cc [new file with mode: 0644]
src/lib/cryptolink/crypto_asym.cc [new file with mode: 0644]
src/lib/cryptolink/crypto_asym.h [new file with mode: 0644]
src/lib/cryptolink/crypto_hash.h
src/lib/cryptolink/crypto_hmac.h
src/lib/cryptolink/cryptolink.cc
src/lib/cryptolink/cryptolink.h
src/lib/cryptolink/openssl_asym.cc [new file with mode: 0644]
src/lib/cryptolink/openssl_link.cc
src/lib/cryptolink/tests/Makefile.am
src/lib/cryptolink/tests/asym_unittests.cc [new file with mode: 0644]
src/lib/cryptolink/tests/hmac_unittests.cc
src/lib/cryptolink/tests/testdata/Kexample.+005+18330.key [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/Kexample.+005+18330.private [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/pkcs8.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/pkcs8ne.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/priv.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/pub.pem [new file with mode: 0644]
src/lib/cryptolink/tests/testdata/x509.pem [new file with mode: 0644]

index 005db8379ca3bb50e78b7d87012a7cdf7ddc1ea0..e7f69ab3feb3c803ae109534fcb44b2885f2432f 100644 (file)
@@ -11,17 +11,20 @@ lib_LTLIBRARIES = libkea-cryptolink.la
 libkea_cryptolink_la_SOURCES = cryptolink.h cryptolink.cc
 libkea_cryptolink_la_SOURCES += crypto_hash.h crypto_hash.cc
 libkea_cryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc
+libkea_cryptolink_la_SOURCES += crypto_asym.h crypto_asym.cc
 if HAVE_BOTAN
 libkea_cryptolink_la_SOURCES += botan_link.cc
 libkea_cryptolink_la_SOURCES += botan_common.h
 libkea_cryptolink_la_SOURCES += botan_hash.cc
 libkea_cryptolink_la_SOURCES += botan_hmac.cc
+libkea_cryptolink_la_SOURCES += botan_asym.cc
 endif
 if HAVE_OPENSSL
 libkea_cryptolink_la_SOURCES += openssl_link.cc
 libkea_cryptolink_la_SOURCES += openssl_common.h
 libkea_cryptolink_la_SOURCES += openssl_hash.cc
 libkea_cryptolink_la_SOURCES += openssl_hmac.cc
+libkea_cryptolink_la_SOURCES += openssl_asym.cc
 endif
 
 libkea_cryptolink_la_LDFLAGS = ${CRYPTO_LDFLAGS}
diff --git a/src/lib/cryptolink/botan_asym.cc b/src/lib/cryptolink/botan_asym.cc
new file mode 100644 (file)
index 0000000..632178f
--- /dev/null
@@ -0,0 +1,1274 @@
+// Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <botan/version.h>
+#include <botan/botan.h>
+#include <botan/hash.h>
+#include <botan/oids.h>
+#include <botan/pubkey.h>
+#include <botan/rsa.h>
+#include <botan/x509stor.h>
+
+#include <util/encode/base64.h>
+#include <cryptolink/botan_common.h>
+
+#include <cstring>
+#include <fstream>
+
+namespace isc {
+namespace cryptolink {
+
+/// @brief Botan implementation of asymmetrical cryptography (Asym).          
+// Each method is the counterpart of the Asym corresponding method.
+class AsymImpl {
+public:
+    /// @brief Constructor from a key, asym and hash algorithm,
+    ///        key kind and key binary format
+    ///
+    /// See constructor of the @ref isc::cryptolink::Asym class for details.
+    ///
+    /// @param key            The key to sign/verify with
+    /// @param len            The length of the key
+    /// @param asym_algorithm The asymmetrical cryptography algorithm
+    /// @param hash_algorithm The hash algorithm
+    /// @param key_kind       The key kind
+    /// @param key_format     The key binary format
+    explicit AsymImpl(const void* key, size_t key_len,
+                      const AsymAlgorithm asym_algorithm,
+                      const HashAlgorithm hash_algorithm,
+                      const AsymKeyKind key_kind,
+                      const AsymFormat key_format) {
+        algo_ = asym_algorithm;
+        hash_ = hash_algorithm;
+        kind_ = key_kind;
+        if (algo_ != RSA_) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown asym algorithm: " <<
+                      static_cast<int>(algo_));
+        }
+        std::string hash = btn::getHashAlgorithmName(hash_);
+        if (hash.compare("Unknown") == 0) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown hash algorithm: " <<
+                      static_cast<int>(hash_));
+        }
+        std::string emsa = "EMSA3(" + hash + ")";
+        if (key_len == 0) {
+            isc_throw(BadKey, "Bad RSA " <<
+                      (kind_ != CERT ? "key" : "cert") <<
+                      " length: 0");
+        }
+
+        if ((kind_ == PRIVATE) && (key_format == BASIC)) {
+            // PKCS#1 Private Key
+            const Botan::byte* keyin =
+                reinterpret_cast<const Botan::byte*>(key);
+            const Botan::MemoryVector<Botan::byte> ber(keyin, key_len);
+            // rsaEncription OID and NULL parameters
+            const Botan::AlgorithmIdentifier alg_id =
+                Botan::AlgorithmIdentifier("1.2.840.113549.1.1.1",
+                    Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+            Botan::AutoSeeded_RNG rng;
+            try {
+                priv_.reset(new Botan::RSA_PrivateKey(alg_id, ber, rng));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "RSA_PrivateKey: " << exc.what());
+            }
+        } else if (kind_ == PRIVATE) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
+            // PKCS#1 Public Key
+            const Botan::byte* keyin =
+                reinterpret_cast<const Botan::byte*>(key);
+            const Botan::MemoryVector<Botan::byte> ber(keyin, key_len);
+            // rsaEncription OID and NULL parameters
+            const Botan::AlgorithmIdentifier alg_id =
+                Botan::AlgorithmIdentifier("1.2.840.113549.1.1.1",
+                    Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+            try {
+                pub_.reset(new Botan::RSA_PublicKey(alg_id, ber));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
+            }
+        } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo
+            const Botan::byte* keyin =
+                reinterpret_cast<const Botan::byte*>(key);
+            Botan::DataSource_Memory source(keyin, key_len);
+            Botan::Public_Key* key;
+            try {
+                key = Botan::X509::load_key(source);
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "X509::load_key: " << exc.what());
+            }
+            if (key->algo_name().compare("RSA") != 0) {
+                delete key;
+                isc_throw(BadKey, "not a RSA Public Key");
+            }
+            pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+            if (!pub_.get()) {
+                delete key;
+                isc_throw(LibraryError, "dynamic_cast");
+            }
+        } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+            // RFC 3110 DNS wire format
+            // key_len == 0 was already checked
+            const uint8_t* p =
+                reinterpret_cast<const uint8_t*>(key);
+            size_t e_bytes = *p++;
+            --key_len;
+            if (e_bytes == 0) {
+                if (key_len < 2) {
+                    isc_throw(BadKey,
+                              "Bad RSA Public Key: short exponent length");
+                }
+                e_bytes = (*p++) << 8;
+                e_bytes += *p++;
+                key_len -= 2;
+            }
+            if (key_len < e_bytes) {
+                isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+            }
+            if ((key_len - e_bytes) < 64) {
+                isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            if ((key_len - e_bytes) > 512) {
+                isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            Botan::BigInt e(p, e_bytes);
+            p += e_bytes;
+            key_len -= e_bytes;
+            Botan::BigInt n(p, key_len);
+            try {
+                pub_.reset(new Botan::RSA_PublicKey(n, e));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
+            }
+        } else if (kind_ == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // X.509 Public Key Certificate
+            const Botan::byte* keyin =
+                reinterpret_cast<const Botan::byte*>(key);
+            Botan::DataSource_Memory source(keyin, key_len);
+            try {
+                x509_.reset(new Botan::X509_Certificate(source));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "X509_Certificate: " << exc.what());
+            }
+            const Botan::AlgorithmIdentifier
+                sig_algo(x509_.get()->signature_algorithm());
+            if (hash_ == MD5) {
+                const Botan::AlgorithmIdentifier
+                    rsa_md5("1.2.840.113549.1.1.4",
+                            Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_md5) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA MD5 certificate");
+                }
+            } else if (hash_ == SHA1) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha1("1.2.840.113549.1.1.5",
+                             Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha1) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA1 certificate");
+                }
+            } else if (hash_ == SHA224) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha224("1.2.840.113549.1.1.14",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha224) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA224 certificate");
+                }
+            } else if (hash_ == SHA256) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha256("1.2.840.113549.1.1.11",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha256) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA256 certificate");
+                }
+            } else if (hash_ == SHA384) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha384("1.2.840.113549.1.1.12",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha384) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA384 certificate");
+                }
+            } else if (hash_ == SHA512) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha512("1.2.840.113549.1.1.13",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha512) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA512 certificate");
+                }
+            } else {
+                x509_.reset();
+                isc_throw(UnsupportedAlgorithm,
+                          "Bad hash algorithm for certificate: " <<
+                          static_cast<int>(hash_));
+            }
+            Botan::Public_Key* key = x509_.get()->subject_public_key();
+            if (key->algo_name().compare("RSA") != 0) {
+                delete key;
+                x509_.reset();
+                isc_throw(BadKey, "not a RSA Certificate");
+            }
+            pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+            if (!pub_.get()) {
+                delete key;
+                x509_.reset();
+                isc_throw(LibraryError, "dynamic_cast");
+            }
+        } else if (kind_ == CERT) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+
+        if (kind_ == PRIVATE) {
+            try {
+                signer_.reset(new Botan::PK_Signer(*priv_.get(), emsa));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "PK_Signer: " << exc.what());
+            }
+        } else {
+            try {
+                verifier_.reset(new Botan::PK_Verifier(*pub_.get(), emsa));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "PK_Verifier: " << exc.what());
+            }
+        }
+    }
+
+    /// @brief Constructor from a key file with password,
+    ///        asym and hash algorithm, key kind and key binary format
+    ///
+    /// See constructor of the @ref isc::cryptolink::Asym class for details.
+    ///
+    /// @param filename       The key file name/path
+    /// @param password       The PKCS#8 password
+    /// @param asym_algorithm The asymmetrical cryptography algorithm
+    /// @param hash_algorithm The hash algorithm
+    /// @param key_kind       The key kind
+    /// @param key_format     The key binary format
+    explicit AsymImpl(const std::string& filename,
+                      const std::string& password,
+                      const AsymAlgorithm asym_algorithm,
+                      const HashAlgorithm hash_algorithm,
+                      const AsymKeyKind key_kind,
+                      const AsymFormat key_format) {
+        algo_ = asym_algorithm;
+        hash_ = hash_algorithm;
+        kind_ = key_kind;
+        if (algo_ != RSA_) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown asym algorithm: " <<
+                      static_cast<int>(algo_));
+        }
+        std::string hash = btn::getHashAlgorithmName(hash_);
+        if (hash.compare("Unknown") == 0) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown hash algorithm: " <<
+                      static_cast<int>(hash_));
+        }
+        std::string emsa = "EMSA3(" + hash + ")";
+
+        if ((kind_ == PRIVATE) && (key_format == ASN1)) {
+            // PKCS#8 Private Key PEM file
+            Botan::Private_Key* key;
+            Botan::AutoSeeded_RNG rng;
+            try {
+                key = Botan::PKCS8::load_key(filename, rng, password);
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "PKCS8::load_key: " << exc.what());
+            }
+            if (key->algo_name().compare("RSA") != 0) {
+                delete key;
+                isc_throw(BadKey, "not a RSA Private Key");
+            }
+            priv_.reset(dynamic_cast<Botan::RSA_PrivateKey*>(key));
+            if (!priv_.get()) {
+                delete key;
+                isc_throw(LibraryError, "dynamic_cast");
+            }
+        } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
+            // bind9 .private file
+            // warn when password not empty
+            if ((hash_ != MD5) && (hash_ != SHA1) &&
+                (hash_ != SHA256) && (hash_ != SHA512)) {
+                isc_throw(UnsupportedAlgorithm,
+                          "Not compatible hash algorithm: " <<
+                          static_cast<int>(hash_));
+            }
+            std::ifstream fp(filename.c_str(), std::ios::in);
+            if (!fp.is_open()) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            bool got_algorithm = false;
+            bool got_modulus = false;
+            bool got_pub_exponent = false;
+            bool got_priv_exponent = false;
+            bool got_prime1 = false;
+            bool got_prime2 = false;
+            Botan::BigInt n;
+            Botan::BigInt e;
+            Botan::BigInt d;
+            Botan::BigInt p;
+            Botan::BigInt q;
+            while (fp.good()) {
+                std::string line;
+                getline(fp, line);
+                if (line.find("Algorithm:") == 0) {
+                    if (got_algorithm) {
+                        fp.close();
+                        isc_throw(BadKey, "Two Algorithm entries");
+                    }
+                    got_algorithm = true;
+                    std::string value = line.substr(strlen("Algorithm:") + 1,
+                                                    std::string::npos);
+                    int alg = std::atoi(value.c_str());
+                    if (alg == 1) {
+                        // RSAMD5
+                        if (hash_ != MD5) {
+                            fp.close();
+                            isc_throw(BadKey, "Require a RSA MD5 key");
+                        }
+                    } else if ((alg == 5) || (alg == 7)) {
+                        // RSASHA1 or RSASHA1-NSEC3-SHA1
+                        if (hash_ != SHA1) {
+                            fp.close();
+                            isc_throw(BadKey, "Require a RSA SHA1 key");
+                        }
+                    } else if (alg == 8) {
+                        // RSASHA256
+                        if (hash_ != SHA256) {
+                            fp.close();
+                            isc_throw(BadKey, "Require a RSA SHA256 key");
+                        }
+                    } else if (alg == 10) {
+                        // RSASHA512
+                        if (hash_ != SHA512) {
+                            fp.close();
+                            isc_throw(BadKey, "Require a RSA SHA512 key");
+                        }
+                    } else {
+                        fp.close();
+                        isc_throw(BadKey, "Bad Algorithm: " << alg);
+                    }
+                } else if (line.find("Modulus:") == 0) {
+                    if (got_modulus) {
+                        fp.close();
+                        isc_throw(BadKey, "Two Modulus entries");
+                    }
+                    got_modulus = true;
+                    std::string value = line.substr(strlen("Modulus:") + 1,
+                                                    std::string::npos);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        fp.close();
+                        isc_throw(BadKey, "Modulus: " << exc.what());
+                    }
+                    n = Botan::BigInt(&bin[0], bin.size());
+              } else if (line.find("PublicExponent:") == 0) {
+                    if (got_pub_exponent) {
+                        fp.close();
+                        isc_throw(BadKey, "Two PublicExponent entries");
+                    }
+                    got_pub_exponent = true;
+                    std::string value =
+                        line.substr(strlen("PublicExponent:") + 1,
+                                    std::string::npos);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        fp.close();
+                        isc_throw(BadKey, "PublicExponent: " << exc.what());
+                    }
+                    e = Botan::BigInt(&bin[0], bin.size());
+                } else if (line.find("PrivateExponent:") == 0) {
+                    if (got_priv_exponent) {
+                        fp.close();
+                        isc_throw(BadKey, "Two PrivateExponent entries");
+                    }
+                    got_priv_exponent = true;
+                    std::string value =
+                        line.substr(strlen("PrivateExponent:") + 1,
+                                    std::string::npos);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        fp.close();
+                        isc_throw(BadKey, "PrivateExponent: " << exc.what());
+                    }
+                    d = Botan::BigInt(&bin[0], bin.size());
+                } else if (line.find("Prime1:") == 0) {
+                    if (got_prime1) {
+                        fp.close();
+                        isc_throw(BadKey, "Two Prime1 entries");
+                    }
+                    got_prime1 = true;
+                    std::string value = line.substr(strlen("Prime1:") + 1,
+                                                    std::string::npos);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        fp.close();
+                        isc_throw(BadKey, "Prime1: " << exc.what());
+                    }
+                    p = Botan::BigInt(&bin[0], bin.size());
+                } else if (line.find("Prime2:") == 0) {
+                    if (got_prime2) {
+                        fp.close();
+                        isc_throw(BadKey, "Two Prime2 entries");
+                    }
+                    got_prime2 = true;
+                    std::string value = line.substr(strlen("Prime2:") + 1,
+                                                    std::string::npos);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        fp.close();
+                        isc_throw(BadKey, "Prime2: " << exc.what());
+                    }
+                    q = Botan::BigInt(&bin[0], bin.size());
+                }
+            }
+            fp.close();
+            if (!got_algorithm) {
+                isc_throw(BadKey, "Missing Algorithm entry");
+            }
+            if (!got_modulus) {
+                isc_throw(BadKey, "Missing Modulus entry");
+            }
+            if (!got_pub_exponent) {
+                isc_throw(BadKey, "Missing PublicExponent entry");
+            }
+            if (!got_priv_exponent) {
+                isc_throw(BadKey, "Missing PrivateExponent entry");
+            }
+            if (!got_prime1) {
+                isc_throw(BadKey, "Missing Prime1 entry");
+            }
+            if (!got_prime2) {
+                isc_throw(BadKey, "Missing Prime2 entry");
+            }
+            Botan::AutoSeeded_RNG rng;
+            try {
+                priv_.reset(new Botan::RSA_PrivateKey(rng, p, q, e, d, n));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "RSA_PrivateKey" << exc.what());
+            }
+        } else if (kind_ == PRIVATE) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo PEM file
+            // warn when password not empty
+            Botan::Public_Key* key;
+            try {
+                key = Botan::X509::load_key(filename);
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "X509::load_key: " << exc.what());
+            }
+            if (key->algo_name().compare("RSA") != 0) {
+                delete key;
+                isc_throw(BadKey, "not a RSA Public Key");
+            }
+            pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+            if (!pub_.get()) {
+                delete key;
+                isc_throw(LibraryError, "dynamic_cast");
+            }
+        } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+            // bind9 .key file (RDATA)
+            // warn when password not empty
+            if ((hash_ != MD5) && (hash_ != SHA1) &&
+                (hash_ != SHA256) && (hash_ != SHA512)) {
+                isc_throw(UnsupportedAlgorithm,
+                          "Not compatible hash algorithm: " <<
+                          static_cast<int>(hash_));
+            }
+            std::ifstream fp(filename.c_str(), std::ios::in);
+            if (!fp.is_open()) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            std::string line;
+            bool found = false;
+            while (fp.good()) {
+                getline(fp, line);
+                if (line.empty() || (line[0] == ';')) {
+                    continue;
+                }
+                if (line.find("DNSKEY") == std::string::npos) {
+                    continue;
+                }
+                found = true;
+                if (line[line.size() - 1] == '\n') {
+                    line.erase(line.size() - 1);
+                }
+                break;
+            }
+            fp.close();
+            if (!found) {
+                isc_throw(BadKey, "Can't find a DNSKEY");
+            }
+            const std::string b64 =
+                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
+                "ghijklmnopqrstuvwxyz0123456789+/=";
+            const std::string value = line.substr(line.find_last_not_of(b64));
+            std::vector<uint8_t> bin;
+            try {
+                util::encode::decodeBase64(value, bin);
+            } catch (const BadValue& exc) {
+               isc_throw(BadKey, "Can't decode base64: " << exc.what());
+            }
+            size_t key_len = bin.size();
+            const uint8_t* p = &bin[0];
+            size_t e_bytes = *p++;
+            --key_len;
+            if (e_bytes == 0) {
+                if (key_len < 2) {
+                    isc_throw(BadKey,
+                              "Bad RSA Public Key: short exponent length");
+                }
+                e_bytes = (*p++) << 8;
+                e_bytes += *p++;
+                key_len -= 2;
+            }
+            if (key_len < e_bytes) {
+                isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+            }
+            if ((key_len - e_bytes) < 64) {
+                isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            if ((key_len - e_bytes) > 512) {
+                isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            Botan::BigInt e(p, e_bytes);
+            p += e_bytes;
+            key_len -= e_bytes;
+            Botan::BigInt n(p, key_len);
+            try {
+                pub_.reset(new Botan::RSA_PublicKey(n, e));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
+            }
+        } else if (kind_ == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // X.509 Public Key Certificate PEM file
+            // warn when password not empty
+            try {
+                x509_.reset(new Botan::X509_Certificate(filename));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "X509_Certificate: " << exc.what());
+            }
+            const Botan::AlgorithmIdentifier
+                sig_algo(x509_.get()->signature_algorithm());
+            if (hash_ == MD5) {
+                const Botan::AlgorithmIdentifier
+                    rsa_md5("1.2.840.113549.1.1.4",
+                            Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_md5) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA MD5 certificate");
+                }
+            } else if (hash_ == SHA1) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha1("1.2.840.113549.1.1.5",
+                             Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha1) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA1 certificate");
+                }
+            } else if (hash_ == SHA224) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha224("1.2.840.113549.1.1.14",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha224) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA224 certificate");
+                }
+            } else if (hash_ == SHA256) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha256("1.2.840.113549.1.1.11",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha256) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA256 certificate");
+                }
+            } else if (hash_ == SHA384) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha384("1.2.840.113549.1.1.12",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha384) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA384 certificate");
+                }
+            } else if (hash_ == SHA512) {
+                const Botan::AlgorithmIdentifier
+                    rsa_sha512("1.2.840.113549.1.1.13",
+                               Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+                if (sig_algo != rsa_sha512) {
+                    x509_.reset();
+                    isc_throw(BadKey, "Require a RSA SHA512 certificate");
+                }
+            } else {
+                x509_.reset();
+                isc_throw(UnsupportedAlgorithm,
+                          "Bad hash algorithm for certificate: " <<
+                          static_cast<int>(hash_));
+            }
+            Botan::Public_Key* key;
+            try {
+                key = x509_.get()->subject_public_key();
+            } catch (const std::exception& exc) {
+                x509_.reset();
+                isc_throw(BadKey, "subject_public_key: " << exc.what());
+            }
+            if (key->algo_name().compare("RSA") != 0) {
+                delete key;
+                x509_.reset();
+                isc_throw(BadKey, "not a RSA Public Key");
+            }
+            pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+            if (!pub_.get()) {
+                delete key;
+                x509_.reset();
+                isc_throw(LibraryError, "dynamic_cast");
+            }
+        } else if (kind_ == CERT) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Public Key Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+
+        if (kind_ == PRIVATE) {
+            try {
+                signer_.reset(new Botan::PK_Signer(*priv_.get(), emsa));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "PK_Signer: " << exc.what());
+            }
+        } else {
+            try {
+                verifier_.reset(new Botan::PK_Verifier(*pub_.get(), emsa));
+            } catch (const std::exception& exc) {
+                isc_throw(BadKey, "PK_Verifier: " << exc.what());
+            }
+        }
+    }
+
+    /// @brief Destructor
+    ~AsymImpl() { }
+
+    /// @brief Returns the key size in bits
+    ///
+    size_t getKeySize() const {
+        if (kind_ == PRIVATE) {
+            return (priv_.get()->get_n().bits());
+        } else {
+            return (pub_.get()->get_n().bits());
+        }
+    }
+
+    /// @brief Returns the output size of the signature
+    ///
+    /// \param sig_format The signature binary format
+    size_t getSignatureLength(const AsymFormat sig_format) const {
+        switch (algo_) {
+        case RSA_:
+            switch (sig_format) {
+            case BASIC:
+            case ASN1:
+            case DNS:
+                // In all cases a big integer of the size of n
+                if (kind_ == PRIVATE) {
+                    return (priv_.get()->get_n().bytes());
+                } else {
+                    return (pub_.get()->get_n().bytes());
+                }
+            default:
+                isc_throw(UnsupportedAlgorithm,
+                          "Unknown RSA Signature format: " <<
+                          static_cast<int>(sig_format));
+            }
+        default:
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown asym algorithm: " <<
+                      static_cast<int>(algo_));
+        }
+    }           
+
+    /// @brief Add data to digest
+    ///
+    /// See @ref isc::cryptolink::Asym::update() for details.
+    void update(const void* data, const size_t len) {
+        try {
+            if (kind_ == PRIVATE) {
+                signer_.get()->update(
+                    reinterpret_cast<const Botan::byte*>(data), len);
+            } else {
+                verifier_.get()->update(
+                    reinterpret_cast<const Botan::byte*>(data), len);
+            }            
+        } catch (const std::exception& exc) {
+            isc_throw(LibraryError, "update: " << exc.what());
+        }
+    }
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::Asym::sign() for details.
+    void sign(isc::util::OutputBuffer& result, size_t len,
+              const AsymFormat) {
+        try {
+            Botan::SecureVector<Botan::byte> b_result;
+            Botan::AutoSeeded_RNG rng;
+            b_result = signer_.get()->signature(rng);
+            if (len > b_result.size()) {
+                len = b_result.size();
+            }
+            result.writeData(b_result.begin(), len);
+        } catch (const std::exception& exc) {
+            isc_throw(LibraryError, "signature: " << exc.what());
+        }
+    }
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::Asym::sign() for details.
+    void sign(void* result, size_t len, const AsymFormat sig_format) {
+        try {
+            Botan::SecureVector<Botan::byte> b_result;
+            Botan::AutoSeeded_RNG rng;
+            b_result = signer_.get()->signature(rng);
+            size_t output_size = getSignatureLength(sig_format);
+            if (output_size > len) {
+                output_size = len;
+            }
+            std::memcpy(result, b_result.begin(), output_size);
+        } catch (const std::exception& exc) {
+            isc_throw(LibraryError, "signature: " << exc.what());
+        }
+    }
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::Asym::sign() for details.
+    std::vector<uint8_t> sign(size_t len, const AsymFormat) {
+        try {
+            Botan::SecureVector<Botan::byte> b_result;
+            Botan::AutoSeeded_RNG rng;
+            b_result = signer_.get()->signature(rng);
+            if (len > b_result.size()) {
+                return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
+            } else {
+                return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
+            }
+        } catch (const std::exception& exc) {
+            isc_throw(LibraryError, "signature: " << exc.what());
+        }
+    }
+
+    /// @brief Verify an existing signature
+    ///
+    /// See @ref isc::cryptolink::Asym::verify() for details.
+    bool verify(const void* sig, size_t len, const AsymFormat sig_format) {
+        size_t size = getSignatureLength(sig_format);
+        if (len != size) {
+            return false;
+        }
+        const Botan::byte* sigbuf = reinterpret_cast<const Botan::byte*>(sig);
+        try {
+            return verifier_.get()->check_signature(sigbuf, len);
+        } catch (const std::exception& exc) {
+            isc_throw(LibraryError, "check_signature: " << exc.what());
+        }
+    }
+
+    /// @brief Export the key value (binary)
+    ///
+    /// See @ref isc::cryptolink::Asym::exportkey() for details
+    std::vector<uint8_t> exportkey(const AsymKeyKind key_kind,
+                                   const AsymFormat key_format) const {
+        if ((key_kind == PRIVATE) && (key_format == BASIC)) {
+            // PKCS#1 Private Key
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            Botan::MemoryVector<Botan::byte> der;
+            try {
+                der = priv_.get()->pkcs8_private_key();
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "pkcs8_private_key: " << exc.what());
+            }
+            return std::vector<uint8_t>(der.begin(), der.end());
+        } else if (key_kind == PRIVATE) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
+            // PKCS#1 Public Key
+            Botan::MemoryVector<Botan::byte> der;
+            try {
+                der = pub_.get()->x509_subject_public_key();
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError,
+                          "x509_subject_public_key: "
+                          << exc.what());
+            }
+            return std::vector<uint8_t>(der.begin(), der.end());
+        } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo
+            Botan::MemoryVector<Botan::byte> ber;
+            try {
+                ber = Botan::X509::BER_encode(*pub_.get());
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "X509::BER_encode: " << exc.what());
+            }
+            return std::vector<uint8_t>(ber.begin(), ber.end());
+        } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+            // RFC 3110 DNS wire format
+            size_t e_bytes = pub_.get()->get_e().bytes();
+            size_t mod_bytes = pub_.get()->get_n().bytes();
+            size_t x_bytes = 1;
+            if (e_bytes >= 256) {
+                x_bytes += 2;
+            }
+            std::vector<uint8_t> rdata(x_bytes + e_bytes + mod_bytes);
+            if (e_bytes < 256) {
+                rdata[0] = e_bytes;
+            } else {
+                rdata[0] = 0;
+                rdata[1] = (e_bytes >> 8) & 0xff;
+                rdata[2] = e_bytes & 0xff;
+            }
+            pub_.get()->get_e().binary_encode(&rdata[x_bytes]);
+            pub_.get()->get_n().binary_encode(&rdata[x_bytes + e_bytes]);
+            return rdata;
+        } else if (kind_ == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // X.509 Public Key Certificate
+            if (kind_ != CERT) {
+                isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+            }
+            Botan::MemoryVector<Botan::byte> ber;
+            try {
+                ber = x509_.get()->BER_encode();
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "BER_encode" << exc.what());
+            }
+            return std::vector<uint8_t>(ber.begin(), ber.end());
+        } else if (kind_ == CERT) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+    }
+
+    /// @brief Export the key value (file)
+    ///
+    /// See @ref isc::cryptolink::Asym::exportkey() for details
+    void exportkey(const std::string& filename,
+                   const std::string& password,
+                   const AsymKeyKind key_kind,
+                   const AsymFormat key_format) const {
+        if ((key_kind == PRIVATE) && (key_format == ASN1)) {
+            // PKCS#8 Private Key PEM file
+            std::string pem;
+            Botan::AutoSeeded_RNG rng;
+            try {
+                pem = Botan::PKCS8::PEM_encode(*priv_.get(), rng,
+                                               password, "AES-128/CBC");
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "PKCS8::PEM_encode: " << exc.what());
+            }
+            std::ofstream fp(filename.c_str(),
+                             std::ofstream::out | std::ofstream::trunc);
+            if (fp.is_open()) {
+                fp.write(pem.c_str(), pem.size());
+                fp.close();
+            } else {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+        } else if ((key_kind == PRIVATE) && (key_format == DNS)) {
+            //  bind9 .private file
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            if ((hash_ != MD5) && (hash_ != SHA1) &&
+                (hash_ != SHA256) && (hash_ != SHA512)) {
+                isc_throw(UnsupportedAlgorithm,
+                          "Not compatible hash algorithm: " <<
+                          static_cast<int>(hash_));
+            }
+            std::ofstream fp(filename.c_str(),
+                             std::ofstream::out | std::ofstream::trunc);
+            if (!fp.is_open()) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            fp << "Private-key-format: v1.2\n";
+            if (hash_ == MD5) {
+                fp << "Algorithm: 1 (RSA)\n";
+            } else if (hash_ == SHA1) {
+                fp << "Algorithm: 5 (RSASHA1)\n";
+            } else if (hash_ == SHA256) {
+                fp << "Algorithm: 8 (RSASHA256)\n";
+            } else if (hash_ == SHA512) {
+                fp << "Algorithm: 10 (RSASHA512)\n";
+            }
+            std::vector<uint8_t> bin;
+            bin.resize(priv_.get()->get_n().bytes());
+            priv_.get()->get_n().binary_encode(&bin[0]);
+            fp << "Modulus: " << util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_e().bytes());
+            priv_.get()->get_e().binary_encode(&bin[0]);
+            fp << "PublicExponent: " <<
+                util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_d().bytes());
+            priv_.get()->get_d().binary_encode(&bin[0]);
+            fp << "PrivateExponent: " <<
+                util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_p().bytes());
+            priv_.get()->get_p().binary_encode(&bin[0]);
+            fp << "Prime1: " << util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_q().bytes());
+            priv_.get()->get_q().binary_encode(&bin[0]);
+            fp << "Prime2: " << util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_d1().bytes());
+            priv_.get()->get_d1().binary_encode(&bin[0]);
+            fp << "Exponent1: " << util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_d2().bytes());
+            priv_.get()->get_d2().binary_encode(&bin[0]);
+            fp << "Exponent2: " << util::encode::encodeBase64(bin) << '\n';
+            bin.resize(priv_.get()->get_c().bytes());
+            priv_.get()->get_c().binary_encode(&bin[0]);
+            fp << "Coefficient: " << util::encode::encodeBase64(bin) << '\n';
+            fp.close();
+        } else if (key_kind == PRIVATE) {
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo PEM file
+            // warn when password not empty
+            std::string pem;
+            try {
+                pem = Botan::X509::PEM_encode(*pub_.get());
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "X509::PEM_encode: " << exc.what());
+            }
+            std::ofstream fp(filename.c_str(),
+                             std::ofstream::out | std::ofstream::trunc);
+            if (fp.is_open()) {
+                fp.write(pem.c_str(), pem.size());
+                fp.close();
+            } else {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+        }  else if ((key_kind == PUBLIC) && (key_format == DNS)) {
+            // bind9 .key file (RDATA)
+            // warn when password not empty
+            std::vector<uint8_t> bin = exportkey(key_kind, key_format);
+            std::ofstream fp(filename.c_str(),
+                             std::ofstream::out | std::ofstream::trunc);
+            if (!fp.is_open()) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            fp << "; DNSKEY RDATA: " <<
+                util::encode::encodeBase64(bin) << '\n';
+            fp.close();
+        } else if (key_kind == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // Public Key Certificate PEM file
+            // warn when password not empty
+            if (!x509_) {
+                isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+            }
+            std::string pem;
+            try {
+                pem = x509_.get()->PEM_encode();
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "PEM_encode: " << exc.what());
+            }
+            std::ofstream fp(filename.c_str(),
+                             std::ofstream::out | std::ofstream::trunc);
+            if (fp.is_open()) {
+                fp.write(pem.c_str(), pem.size());
+                fp.close();
+            } else {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+        } else if (kind_ == CERT) {
+            if (!x509_) {
+                isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+            }
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+    }
+
+    /// @brief Check the validity
+    ///
+    /// See @ref isc::cryptolink::Asym::validate() for details
+    bool validate() const {
+        Botan::AutoSeeded_RNG rng;
+        Botan::X509_Store store;
+        Botan::X509_Code status;
+        switch (kind_) {
+        case PUBLIC:
+            // what to do?
+            try {
+                return pub_.get()->check_key(rng, true);
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "check_key: " << exc.what());
+            }
+        case PRIVATE:
+            try {
+                return priv_.get()->check_key(rng, true);
+            } catch (const std::exception& exc) {
+                isc_throw(LibraryError, "check_key: " << exc.what());
+            }
+        case CERT:
+            store.add_cert(*x509_.get(), true);
+            status = store.validate_cert(*x509_.get());
+            if (status == Botan::VERIFIED) {
+                return true;
+            }
+            return false;
+        default:
+            return false;
+        }
+    }
+
+    /// @brief Compare two keys
+    ///
+    /// See @ref isc::cryptolink::Asym::compare() for details
+    bool compare(const AsymImpl* other, const AsymKeyKind key_kind) const {
+        Botan::BigInt e, n;
+        switch (key_kind) {
+        case CERT:
+            // Special case for cert - cert
+            if ((kind_ == CERT) && (other->kind_ == CERT)) {
+                return (*x509_.get() == *other->x509_.get());
+            }
+            // At least one should be a cert
+            if ((kind_ != CERT) && (other->kind_ != CERT)) {
+                return false;
+            }
+            // For all other cases just compare public keys
+            // Falls into
+        case PUBLIC:
+            if (kind_ == PRIVATE) {
+                e = priv_.get()->get_e();
+                n = priv_.get()->get_n();;
+            } else if ((kind_ == PUBLIC) || (kind_ == CERT)) {
+                e = pub_.get()->get_e();
+                n = pub_.get()->get_n();
+            } else {
+                return false;
+            }
+            if (other->kind_ == PRIVATE) {
+                return ((e == other->priv_.get()->get_e()) &&
+                        (n == other->priv_.get()->get_n()));
+            } else if ((other->kind_ == PUBLIC) || (other->kind_ == CERT)) {
+                return ((e == other->pub_.get()->get_e()) &&
+                        (n == other->pub_.get()->get_n()));
+            } else {
+                return false;
+            }
+        case PRIVATE:
+            if ((kind_ != PRIVATE) || (other->kind_ != PRIVATE)) {
+                return false;
+            }
+            // If public keys match so private too
+            return ((priv_.get()->get_e() == other->priv_.get()->get_e()) &&
+                    (priv_.get()->get_n() == other->priv_.get()->get_n()));
+        default:
+            return false;
+        }
+    }
+
+private:
+    /// @brief The asymmetrical cryptography algorithm
+    AsymAlgorithm algo_;
+    /// @brief The hash algorithm
+    HashAlgorithm hash_;
+    /// @brief The key kind
+    AsymKeyKind kind_;
+    /// @brief The protected pointer to the Botan private key
+    boost::scoped_ptr<Botan::RSA_PrivateKey> priv_;
+    /// @brief The protected pointer to the Botan public key
+    boost::scoped_ptr<Botan::RSA_PublicKey> pub_;
+    /// @brief The protected pointer to the Botan certificate
+    boost::scoped_ptr<Botan::X509_Certificate> x509_;
+    /// @brief The protected pointer to the Botan signer
+    boost::scoped_ptr<Botan::PK_Signer> signer_;
+    /// @brief The protected pointer to the Botan verifier
+    boost::scoped_ptr<Botan::PK_Verifier> verifier_;
+};
+
+Asym::Asym(const void* key, size_t key_len,
+           const AsymAlgorithm asym_algorithm,
+           const HashAlgorithm hash_algorithm,
+           const AsymKeyKind key_kind,
+           const AsymFormat key_format)
+{
+    impl_ = new AsymImpl(key, key_len,
+                         asym_algorithm, hash_algorithm,
+                         key_kind, key_format);
+}
+
+Asym::Asym(const std::vector<uint8_t> key,
+           const AsymAlgorithm asym_algorithm,
+           const HashAlgorithm hash_algorithm,
+           const AsymKeyKind key_kind,
+           const AsymFormat key_format)
+{
+    impl_ = new AsymImpl(&key[0], key.size(),
+                         asym_algorithm, hash_algorithm,
+                         key_kind, key_format);
+}
+
+Asym::Asym(const std::string& filename,
+           const std::string& password,
+           const AsymAlgorithm asym_algorithm,
+           const HashAlgorithm hash_algorithm,
+           const AsymKeyKind key_kind,
+           const AsymFormat key_format)
+{
+    impl_ = new AsymImpl(filename, password,
+                         asym_algorithm, hash_algorithm,
+                         key_kind, key_format);
+}
+
+Asym::~Asym() {
+    delete impl_;
+}
+
+size_t
+Asym::getKeySize() const {
+    return (impl_->getKeySize());
+}
+
+size_t
+Asym::getSignatureLength(const AsymFormat sig_format) const {
+    return (impl_->getSignatureLength(sig_format));
+}
+
+void
+Asym::update(const void* data, const size_t len) {
+    impl_->update(data, len);
+}
+
+void
+Asym::sign(isc::util::OutputBuffer& result, size_t len,
+           const AsymFormat sig_format) {
+    impl_->sign(result, len, sig_format);
+}
+
+void
+Asym::sign(void* result, size_t len, const AsymFormat sig_format) {
+    impl_->sign(result, len, sig_format);
+}
+
+std::vector<uint8_t>
+Asym::sign(size_t len, const AsymFormat sig_format) {
+    return (impl_->sign(len, sig_format));
+}
+
+bool
+Asym::verify(const void* sig, size_t len, const AsymFormat sig_format) {
+    return (impl_->verify(sig, len, sig_format));
+}
+
+std::vector<uint8_t>
+Asym::exportkey(const AsymKeyKind key_kind,
+                const AsymFormat key_format) const {
+    return (impl_->exportkey(key_kind, key_format));
+}
+
+void
+Asym::exportkey(const std::string& filename,
+                const std::string& password,
+                const AsymKeyKind key_kind,
+                const AsymFormat key_format) const {
+    impl_->exportkey(filename, password, key_kind, key_format);
+}
+
+bool
+Asym::validate() const {
+    return (impl_->validate());
+}
+
+bool
+Asym::compare(const Asym* other, const AsymKeyKind key_kind) const {
+    return (impl_->compare(other->impl_, key_kind));
+}
+
+} // namespace cryptolink
+} // namespace isc
diff --git a/src/lib/cryptolink/crypto_asym.cc b/src/lib/cryptolink/crypto_asym.cc
new file mode 100644 (file)
index 0000000..8ebbc44
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <cstring>
+
+namespace isc {
+namespace cryptolink {
+
+void
+deleteAsym(Asym* asym) {
+    delete asym;
+}
+
+} // namespace cryptolink
+} // namespace isc
diff --git a/src/lib/cryptolink/crypto_asym.h b/src/lib/cryptolink/crypto_asym.h
new file mode 100644 (file)
index 0000000..49ea268
--- /dev/null
@@ -0,0 +1,239 @@
+// Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/buffer.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <cryptolink/cryptolink.h>
+
+#ifndef ISC_CRYPTO_ASYM_H
+#define ISC_CRYPTO_ASYM_H
+
+namespace isc {
+namespace cryptolink {
+
+/// Forward declaration, pimpl style
+class AsymImpl;
+
+/// \brief Asymmetrical cryptography (Asym) support
+///
+/// This class is used to create and verify Asym signatures. Instances
+/// can be created with CryptoLink::createAsym()
+///
+class Asym : private boost::noncopyable {
+private:
+    /// \brief Constructor from a key, asym and hash algorithm,
+    ///        key kind and key binary format
+    ///
+    /// \exception UnsupportedAlgorithm if the given algorithm
+    ///            is unknown or not supported by the underlying library
+    /// \exception Badkey if the given key length is bad
+    /// \exception InvalidCert if the certification fails to validate
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param key            The key to sign/verify with
+    /// \param len            The length of the key
+    /// \param asym_algorithm The asymmetrical cryptography algorithm
+    /// \param hash_algorithm The hash algorithm
+    /// \param key_kind       The key kind
+    /// \param key_format     The key binary format
+    Asym(const void* key, size_t key_len,
+        const AsymAlgorithm asym_algorithm,
+        const HashAlgorithm hash_algorithm,
+        const AsymKeyKind key_kind,
+        const AsymFormat key_format);
+    ///
+    /// \param key            The key to sign/verify with
+    /// \param asym_algorithm The asymmetrical cryptography algorithm
+    /// \param hash_algorithm The hash algorithm
+    /// \param key_kind       The key kind
+    /// \param key_format     The key binary format
+    Asym(const std::vector<uint8_t> key,
+        const AsymAlgorithm asym_algorithm,
+        const HashAlgorithm hash_algorithm,
+        const AsymKeyKind key_kind,
+        const AsymFormat key_format);
+    ///
+    /// \param filename       The key file name/path
+    /// \param password       The PKCS#8 password
+    /// \param asym_algorithm The asymmetrical cryptography algorithm
+    /// \param hash_algorithm The hash algorithm
+    /// \param key_kind       The key kind
+    /// \param file_format    The key file format
+    Asym(const std::string& filename, const std::string& password,
+        const AsymAlgorithm asym_algorithm,
+         const HashAlgorithm hash_algorithm,
+         const AsymKeyKind key_kind,
+        const AsymFormat file_format);
+
+    friend Asym* CryptoLink::createAsym(const void*, size_t,
+                                       const AsymAlgorithm,
+                                       const HashAlgorithm,
+                                       const AsymKeyKind,
+                                       const AsymFormat);
+
+    friend Asym* CryptoLink::createAsym(const std::vector<uint8_t>,
+                                       const AsymAlgorithm,
+                                       const HashAlgorithm,
+                                       const AsymKeyKind,
+                                       const AsymFormat);
+
+    friend Asym* CryptoLink::createAsym(const std::string&,
+                                       const std::string&,
+                                       const AsymAlgorithm,
+                                       const HashAlgorithm,
+                                       const AsymKeyKind,
+                                       const AsymFormat);
+
+public:
+    /// \brief Destructor
+    ~Asym();
+
+    /// \brief Returns the key size in bits
+    ///
+    size_t getKeySize() const;
+
+    /// \brief Returns the output size of the signature
+    ///
+    /// \param sig_format The signature binary format
+    size_t getSignatureLength(const AsymFormat sig_format) const;
+
+    /// \brief Add data to digest
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param data The data to add
+    /// \param len  The size of the data
+    void update(const void* data, const size_t len);
+
+    /// \brief Calculate the final signature
+    ///
+    /// The result will be appended to the given outputbuffer
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param result     The OutputBuffer to append the result to
+    /// \param len        The number of bytes from the result to copy. If this
+    ///                   value is smaller than the algorithms output size, the
+    ///                   result will be truncated. If this value is larger,
+    ///                   only output size bytes will be copied
+    /// \param sig_format The signature binary format
+    void sign(isc::util::OutputBuffer& result, size_t len,
+             const AsymFormat sig_format);
+
+    /// \brief Calculate the final signature
+    ///
+    /// len bytes of data from the result will be copied to *result
+    /// If len is larger than the output size, only output_size bytes
+    /// will be copied. If it is smaller, the output will be truncated
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// At least len bytes of data must be available for writing at
+    /// result
+    void sign(void* result, size_t len, const AsymFormat sig_format);
+
+    /// \brief Calculate the final signatre
+    ///
+    /// The result will be returned as a std::vector<uint8_t>
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param len        The number of bytes from the result to copy. If this
+    ///                   value is smaller than the algorithms output size, the
+    ///                   result will be truncated. If this value is larger,
+    ///                   only output size bytes will be copied
+    /// \param sig_format The signature binary format
+    /// \return           a vector containing the signature
+    std::vector<uint8_t> sign(size_t len, const AsymFormat sig_format);
+
+    /// \brief Verify an existing signature
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param sig        The signature to verify
+    /// \param len        The length of the signature. If this is smaller
+    ///                   than the output length of the algorithm,
+    ///                   only len bytes will be checked
+    /// \param sig_format The signature binary format
+    /// \return           true if the signature is correct, false otherwise
+    ///
+    /// \note verify() does not destroy its context so it can be
+    /// called multiple times with different signatures.
+    bool verify(const void* sig, size_t len, const AsymFormat sig_format);
+
+    /// \brief Export the key value
+    ///
+    /// The result will be returned as a std::vector<uint8_t>
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param key_kind       The key kind
+    /// \param key_format     The key binary format
+    std::vector<uint8_t> exportkey(const AsymKeyKind key_kind,
+                                  const AsymFormat key_format) const;
+
+    /// \brief Export the key value
+    ///
+    /// The key value will be returned into the given file,
+    /// optionally when PKCS#8 is selected encrypted by the password
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param filename       The key file name/path
+    /// \param password       The PKCS#8 password
+    /// \param key_kind       The key kind    
+    /// \param file_format    The key file format
+    void exportkey(const std::string& filename,
+                  const std::string& password,
+                  const AsymKeyKind key_kind,
+                  const AsymFormat file_format) const;
+
+    /// \brief Check the validity
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    bool validate() const;
+    
+    /// \brief Compare two keys
+    ///
+    /// The key kind parameter allows to select what to compare.
+    ///
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    ///
+    /// \param key_kind       The key kind
+    bool compare(const Asym* other, const AsymKeyKind key_kind) const;
+
+private:
+    AsymImpl* impl_;
+};
+
+/// \brief Delete an Asym object
+void deleteAsym(Asym* asym);
+
+} // namespace cryptolink
+} // namespace isc
+   
+#endif // ISC_CRYPTO_ASYM_H
index 7e950d578c9d6d72d05c63356efccfa7603b6909..2ad2e6894d6064394a33b6a2ea75d15869a2a6e0 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014, 2015  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -39,7 +39,7 @@ private:
     /// \exception UnsupportedAlgorithmException if the given algorithm
     ///            is unknown or not supported by the underlying library
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param hash_algorithm The hash algorithm
     Hash(const HashAlgorithm hash_algorithm);
@@ -58,10 +58,10 @@ public:
     /// \brief Add data to digest
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param data The data to add
-    /// \param len The size of the data
+    /// \param len  The size of the data
     void update(const void* data, const size_t len);
 
     /// \brief Calculate the final digest
@@ -69,13 +69,13 @@ public:
     /// The result will be appended to the given outputbuffer
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param result The OutputBuffer to append the result to
-    /// \param len The number of bytes from the result to copy. If this
-    ///        value is smaller than the algorithms output size, the
-    ///        result will be truncated. If this value is larger,
-    ///        only output size bytes will be copied
+    /// \param len    The number of bytes from the result to copy. If this
+    ///               value is smaller than the algorithms output size, the
+    ///               result will be truncated. If this value is larger,
+    ///               only output size bytes will be copied
     void final(isc::util::OutputBuffer& result, size_t len);
 
     /// \brief Calculate the final digest
@@ -85,13 +85,13 @@ public:
     /// will be copied. If it is smaller, the output will be truncated
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// At least len bytes of data must be available for writing at
     /// result.
     ///
     /// \param result The memory location the digest will be written to
-    /// \param len Specifies the size of the result location available
+    /// \param len    Specifies the size of the result location available
     void final(void* result, size_t len);
 
     /// \brief Calculate the final digest
@@ -99,13 +99,13 @@ public:
     /// The result will be returned as a std::vector<uint8_t>
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param len The number of bytes from the result to copy. If this
-    ///        value is smaller than the algorithms output size, the
-    ///        result will be truncated. If this value is larger,
-    ///        only output size bytes will be copied
-    /// \return a vector containing the signature
+    ///            value is smaller than the algorithms output size, the
+    ///            result will be truncated. If this value is larger,
+    ///            only output size bytes will be copied
+    /// \return    a vector containing the signature
     std::vector<uint8_t> final(size_t len);
 
 private:
@@ -122,15 +122,14 @@ private:
 /// \exception UnsupportedAlgorithm if the given algorithm is unknown
 ///            or not supported by the underlying library
 /// \exception LibraryError if there was any unexpected exception
-///                         in the underlying library
+///            in the underlying library
 ///
-/// \param data The data to digest
-/// \param data_len The length of the data
+/// \param data           The data to digest
+/// \param data_len       The length of the data
 /// \param hash_algorithm The hash algorithm
-/// \param result The digest will be appended to this buffer
-/// \param len If this is non-zero and less than the output size, the result
-///            will be truncated to len bytes. If greater than output size
-///            (or equal to zero) only output size bytes are written
+/// \param result         The digest will be appended to this buffer
+/// \param len            If this is non-zero and less than the output size,
+///                       the result will be truncated to len bytes
 void digest(const void* data,
             const size_t data_len,
             const HashAlgorithm hash_algorithm,
index 459b59e825de799b95dbacf7a86ca2a0f0299ebe..be15e7ada0ddac76665a1e8f814ff34d2e16f157 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2011, 2014  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2014, 2015  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -36,19 +36,19 @@ class HMAC : private boost::noncopyable {
 private:
     /// \brief Constructor from a secret and a hash algorithm
     ///
-    /// \exception UnsupportedAlgorithmException if the given algorithm
+    /// \exception UnsupportedAlgorithm if the given algorithm
     ///            is unknown or not supported by the underlying library
-    /// \exception InvalidKeyLength if the given key secret_len is bad
+    /// \exception Badkey if the given key secret_len is bad
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// Notes: if the secret is longer than the block size of its
     /// algorithm, the constructor will run it through the hash
     /// algorithm, and use the digest as the secret for this HMAC
     /// operation
     ///
-    /// \param secret The secret to sign with
-    /// \param len The length of the secret
+    /// \param secret         The secret to sign with
+    /// \param len            The length of the secret
     /// \param hash_algorithm The hash algorithm
     HMAC(const void* secret, size_t secret_len,
          const HashAlgorithm hash_algorithm);
@@ -68,10 +68,10 @@ public:
     /// \brief Add data to digest
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param data The data to add
-    /// \param len The size of the data
+    /// \param len  The size of the data
     void update(const void* data, const size_t len);
 
     /// \brief Calculate the final signature
@@ -79,13 +79,13 @@ public:
     /// The result will be appended to the given outputbuffer
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param result The OutputBuffer to append the result to
-    /// \param len The number of bytes from the result to copy. If this
-    ///        value is smaller than the algorithms output size, the
-    ///        result will be truncated. If this value is larger,
-    ///        only output size bytes will be copied
+    /// \param len    The number of bytes from the result to copy. If this
+    ///               value is smaller than the algorithms output size, the
+    ///               result will be truncated. If this value is larger,
+    ///               only output size bytes will be copied
     void sign(isc::util::OutputBuffer& result, size_t len);
 
     /// \brief Calculate the final signature
@@ -95,7 +95,7 @@ public:
     /// will be copied. If it is smaller, the output will be truncated
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// At least len bytes of data must be available for writing at
     /// result
@@ -106,25 +106,25 @@ public:
     /// The result will be returned as a std::vector<uint8_t>
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param len The number of bytes from the result to copy. If this
-    ///        value is smaller than the algorithms output size, the
-    ///        result will be truncated. If this value is larger,
-    ///        only output size bytes will be copied
-    /// \return a vector containing the signature
+    ///            value is smaller than the algorithms output size, the
+    ///            result will be truncated. If this value is larger,
+    ///            only output size bytes will be copied
+    /// \return    a vector containing the signature
     std::vector<uint8_t> sign(size_t len);
 
     /// \brief Verify an existing signature
     ///
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param sig The signature to verify
     /// \param len The length of the signature. If this is smaller
     ///            than the output length of the algorithm,
     ///            only len bytes will be checked
-    /// \return true if the signature is correct, false otherwise
+    /// \return    true if the signature is correct, false otherwise
     ///
     /// \note verify() does not destroy its context so it can be
     /// called multiple times with different signatures.
@@ -145,22 +145,21 @@ private:
 ///            or not supported by the underlying library
 /// \exception BadKey if the given key secret_len is bad
 /// \exception LibraryError if there was any unexpected exception
-///                         in the underlying library
+///            in the underlying library
 ///
 /// Notes: if the secret is longer than the block size of its
 /// algorithm, the constructor will run it through the hash
 /// algorithm, and use the digest as the secret for this HMAC
 /// operation
 ///
-/// \param data The data to sign
-/// \param data_len The length of the data
-/// \param secret The secret to sign with
-/// \param secret_len The length of the secret
+/// \param data           The data to sign
+/// \param data_len       The length of the data
+/// \param secret         The secret to sign with
+/// \param secret_len     The length of the secret
 /// \param hash_algorithm The hash algorithm
-/// \param result The signature will be appended to this buffer
-/// \param len If this is non-zero and less than the output size, the result
-///            will be truncated to len bytes. If greater than output size
-///            (or equal to zero) only output size bytes are written
+/// \param result         The signature will be appended to this buffer
+/// \param len            If this is non-zero and less than the output size,
+///                       the result will be truncated to len bytes
 void signHMAC(const void* data,
               const size_t data_len,
               const void* secret,
@@ -181,21 +180,21 @@ void signHMAC(const void* data,
 ///            or not supported by the underlying library
 /// \exception BadKey if the given key secret_len is bad
 /// \exception LibraryError if there was any unexpected exception
-///                         in the underlying library
+///            in the underlying library
 ///
 /// Notes: if the secret is longer than the block size of its
 /// algorithm, the constructor will run it through the hash
 /// algorithm, and use the digest as the secret for this HMAC
 /// operation
 ///
-/// \param data The data to verify
-/// \param data_len The length of the data
-/// \param secret The secret to sign with
-/// \param secret_len The length of the secret
+/// \param data           The data to verify
+/// \param data_len       The length of the data
+/// \param secret         The secret to sign with
+/// \param secret_len     The length of the secret
 /// \param hash_algorithm The hash algorithm
-/// \param sig The signature to verify
-/// \param sig_len The length of the signature
-/// \return True if the signature verifies, false if not
+/// \param sig            The signature to verify
+/// \param sig_len        The length of the signature
+/// \return               True if the signature verifies, false if not
 bool verifyHMAC(const void* data,
                 const size_t data_len,
                 const void* secret,
index 799b6710c98fdf3d2d7ad233a56c4aaf7a7ac2ae..df030bfc4c64dd24fb4a160e607888a55273643a 100644 (file)
@@ -15,6 +15,7 @@
 #include <cryptolink/cryptolink.h>
 #include <cryptolink/crypto_hash.h>
 #include <cryptolink/crypto_hmac.h>
+#include <cryptolink/crypto_asym.h>
 
 namespace isc {
 namespace cryptolink {
@@ -47,6 +48,29 @@ CryptoLink::createHMAC(const void* secret, size_t secret_len,
     return (new HMAC(secret, secret_len, hash_algorithm));
 }
 
+Asym*
+CryptoLink::createAsym(const void* key, size_t key_len,
+                      const AsymAlgorithm asym_algorithm,
+                      const HashAlgorithm hash_algorithm,
+                      const AsymKeyKind key_kind,
+                      const AsymFormat key_format)
+{
+    return (new Asym(key, key_len, asym_algorithm, hash_algorithm,
+                    key_kind, key_format));
+}
+
+Asym*
+CryptoLink::createAsym(const std::string& filename,
+                      const std::string& password,
+                      const AsymAlgorithm asym_algorithm,
+                      const HashAlgorithm hash_algorithm,
+                      const AsymKeyKind key_kind,
+                      const AsymFormat file_format)
+{
+    return (new Asym(filename, password, asym_algorithm, hash_algorithm,
+                    key_kind, file_format));
+}
+
 } // namespace cryptolink
 } // namespace isc
 
index b9490e7c777bb70abe6634c31aa36764b8640ca7..e609860b3ac04655c55c566a0d6cccb03751d37a 100644 (file)
@@ -29,27 +29,51 @@ namespace cryptolink {
 
 /// \brief Hash algorithm identifiers
 enum HashAlgorithm {
-    UNKNOWN_HASH = 0,   ///< This value can be used in conversion
-                        ///  functions, to be returned when the
-                        ///  input is unknown (but a value MUST be
-                        ///  returned), for instance when the input
-                        ///  is a Name or a string, and the return
-                        ///  value is a HashAlgorithm.
-    MD5 = 1,            ///< MD5
-    SHA1 = 2,           ///< SHA-1
-    SHA256 = 3,         ///< SHA-256
-    SHA224 = 4,         ///< SHA-224
-    SHA384 = 5,         ///< SHA-384
-    SHA512 = 6          ///< SHA-512
+    UNKNOWN_HASH = 0,    ///< This value can be used in conversion
+                         ///  functions, to be returned when the
+                         ///  input is unknown (but a value MUST be
+                         ///  returned), for instance when the input
+                         ///  is a Name or a string, and the return
+                         ///  value is a HashAlgorithm.
+    MD5 = 1,             ///< MD5
+    SHA1 = 2,            ///< SHA-1
+    SHA256 = 3,          ///< SHA-256
+    SHA224 = 4,          ///< SHA-224
+    SHA384 = 5,          ///< SHA-384
+    SHA512 = 6           ///< SHA-512
 
 };
 
+/// \brief Asymmetrical cryptography algorithm identifiers
+enum AsymAlgorithm {
+    DSA_ = 0,            ///< DSA
+    RSA_ = 1,            ///< RSA
+    ECDSA_ = 2           ///< ECDSA (NIST P-256 and P-384 curves)
+};
+
+/// \brief Asymmetrical cryptography key kinds
+enum AsymKeyKind {
+    PRIVATE = 0,         ///< Private Key
+    PUBLIC = 1,          ///< Public Key
+    CERT = 2             ///< Public Key Certificate
+};
+
+/// \brief Asymmetrical cryptography format
+enum AsymFormat {
+    BASIC = 0,           ///< simplest format (e.g., PKCS#1)
+    ASN1 = 1,            ///< DER/PEM (PKCS#8, SubjectPublicKeyInfo, ...)
+    DNS = 2              ///< DNS wire format, BIND9 key files,
+};
+
 // Forward declaration for createHash()
 class Hash;
 
 // Forward declaration for createHMAC()
 class HMAC;
 
+// Forward declaration for createAsym()
+class Asym;
+
 /// General exception class that is the base for all crypto-related
 /// exceptions
 class CryptoLinkError : public Exception {
@@ -82,6 +106,13 @@ public:
         CryptoLinkError(file, line, what) {}
 };
 
+/// This exception is thrown when a certification is invalid
+class InvalidCert : public CryptoLinkError {
+public:
+   InvalidCert(const char* file, size_t line, const char* what) :
+       CryptoLinkError(file, line, what) {}
+};
+
 /// This exception is raised when a general error that was not
 /// specifically caught is thrown by the underlying library. It
 /// is replaced by this one so as not have 'external' exceptions
@@ -178,10 +209,10 @@ public:
     /// you can use the function deleteHash() as defined in
     /// crypto_hash.h
     ///
-    /// \exception UnsupportedAlgorithmException if the given algorithm
+    /// \exception UnsupportedAlgorithm if the given algorithm
     ///            is unknown or not supported by the underlying library
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
     /// \param hash_algorithm The hash algorithm
     Hash* createHash(const HashAlgorithm hash_algorithm);
@@ -205,18 +236,74 @@ public:
     /// you can use the function deleteHMAC() as defined in
     /// crypto_hmac.h
     ///
-    /// \exception UnsupportedAlgorithmException if the given algorithm
+    /// \exception UnsupportedAlgorithm if the given algorithm
     ///            is unknown or not supported by the underlying library
-    /// \exception InvalidKeyLength if the given key secret_len is bad
+    /// \exception BadKey if the given key secret_len is bad
     /// \exception LibraryError if there was any unexpected exception
-    ///                         in the underlying library
+    ///            in the underlying library
     ///
-    /// \param secret The secret to sign with
-    /// \param secret_len The length of the secret
+    /// \param secret         The secret to sign with
+    /// \param secret_len     The length of the secret
     /// \param hash_algorithm The hash algorithm
     HMAC* createHMAC(const void* secret, size_t secret_len,
                      const HashAlgorithm hash_algorithm);
 
+    /// \brief Factory function for Asymmetrical cryptography (Asym) objects
+    ///
+    /// CryptoLink objects cannot be constructed directly. This
+    /// function creates a new Asym object usable for signing or
+    /// verification.
+    ///
+    /// The caller is responsible for deleting the object, and it is
+    /// therefore highly recommended to place the return value of this
+    /// function in a scoped_ptr or shared_ptr.
+    ///
+    /// If you want to safely delete objects created with this method,
+    /// you can use the function deleteAsym() as defined in
+    /// crypto_asym.h
+    ///
+    /// \exception UnsupportedAlgorithm if the given algorithm
+    ///            is unknown or not supported by the underlying library
+    /// \exception BadKey if the given key length is bad
+    /// \exception InvalidCert if the certification fails to validate
+    /// \exception LibraryError if there was any unexpected exception
+    ///            in the underlying library
+    /// \param key            The key to sign or verify with
+    /// \param key_len        The length of the key
+    /// \param asym_algorithm The asymmetrical cryptography algorithm
+    /// \param hash_algorithm The hash algorithm
+    /// \param key_kind       The key kind
+    /// \param key_format     The key binary format
+    Asym* createAsym(const void* key, size_t key_len,
+                    const AsymAlgorithm asym_algorithm,
+                    const HashAlgorithm hash_algorithm,
+                    const AsymKeyKind key_kind,
+                    const AsymFormat key_format);
+    ///
+    /// \param key            The key to sign or verify with
+    /// \param asym_algorithm The asymmetrical cryptography algorithm
+    /// \param hash_algorithm The hash algorithm
+    /// \param key_kind       The key kind
+    /// \param key_format     The key binary format
+    Asym* createAsym(const std::vector<uint8_t> key,
+                    const AsymAlgorithm asym_algorithm,
+                    const HashAlgorithm hash_algorithm,
+                    const AsymKeyKind key_kind,
+                    const AsymFormat key_format);
+    ///
+    /// \param filename       The key file name/path
+    /// \param password       The PKCS#8 password
+    /// \param asym_algorithm The asymmetrical cryptography algorithm
+    /// \param hash_algorithm The hash algorithm
+    /// \param key_kind       The key kind
+    /// \param file_format    The key file format
+    Asym* createAsym(const std::string& filename,
+                    const std::string& password,
+                    const AsymAlgorithm asym_algorithm,
+                    const HashAlgorithm hash_algorithm,
+                    const AsymKeyKind key_kind,
+                    const AsymFormat file_format);
+
 private:
     // To enable us to use an optional explicit initialization call,
     // the 'real' instance getter is private
diff --git a/src/lib/cryptolink/openssl_asym.cc b/src/lib/cryptolink/openssl_asym.cc
new file mode 100644 (file)
index 0000000..ce9309b
--- /dev/null
@@ -0,0 +1,1519 @@
+// Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include <util/encode/base64.h>
+#include <cryptolink/openssl_common.h>
+
+#include <cstdio>
+#include <cstring>
+
+namespace isc {
+namespace cryptolink {
+
+/// @brief OpenSSL implementation of asymmetrical cryptography (Asym).
+// Each method is the counterpart of the Asym corresponding method.
+class AsymImpl {
+public:
+    /// @brief Constructor from a key, asym and hash algorithm,
+    ///        key kind and key binary format
+    ///
+    /// See constructor of the @ref isc::cryptolink::Asym class for details.
+    ///
+    /// @param key            The key to sign/verify with
+    /// @param len            The length of the key
+    /// @param asym_algorithm The asymmetrical cryptography algorithm
+    /// @param hash_algorithm The hash algorithm
+    /// @param key_kind       The key kind
+    /// @param key_format     The key binary format
+    explicit AsymImpl(const void* key, size_t key_len,
+                      const AsymAlgorithm asym_algorithm,
+                      const HashAlgorithm hash_algorithm,
+                      const AsymKeyKind key_kind,
+                      const AsymFormat key_format) {
+        algo_ = asym_algorithm;
+        hash_ = hash_algorithm;
+        kind_ = key_kind;
+        pkey_ = NULL;
+        x509_ = NULL;
+        if (algo_ != RSA_) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown asym algorithm: " <<
+                      static_cast<int>(algo_));
+        }
+        const EVP_MD* md = ossl::getHashAlgorithm(hash_);
+        if (md == 0) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown hash algorithm: " <<
+                      static_cast<int>(hash_));
+        }
+        if (key_len == 0) {
+            isc_throw(BadKey, "Bad RSA " <<
+                      (kind_ != CERT ? "key" : "cert") <<
+                      " length: 0");
+        }
+
+        if ((kind_ == PRIVATE) && (key_format == BASIC)) {
+            // PKCS#1 Private Key
+            const unsigned char* p =
+                reinterpret_cast<const unsigned char*>(key);
+            RSA* rsa = d2i_RSAPrivateKey(NULL, &p,
+                                         static_cast<long>(key_len));
+            if (!rsa) {
+                isc_throw(BadKey, "d2i_RSAPrivateKey");
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if (kind_ == PRIVATE) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
+            // PKCS#1 Public Key
+            const unsigned char* p =
+                reinterpret_cast<const unsigned char*>(key);
+            RSA* rsa = d2i_RSAPublicKey(NULL, &p,
+                                        static_cast<long>(key_len));
+            if (!rsa) {
+                isc_throw(BadKey, "d2i_RSAPublicKey");
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo
+            const unsigned char* p =
+              reinterpret_cast<const unsigned char*>(key);
+            RSA* rsa = d2i_RSA_PUBKEY(NULL, &p, static_cast<long>(key_len));
+            if (!rsa) {
+                isc_throw(BadKey, "d2i_RSA_PUBKEY");
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+            // RFC 3110 DNS wire format
+            // key_len == 0 was already checked
+            const uint8_t* p =
+                reinterpret_cast<const uint8_t*>(key);
+            unsigned int e_bytes = *p++;
+            --key_len;
+            if (e_bytes == 0) {
+                if (key_len < 2) {
+                    isc_throw(BadKey,
+                              "Bad RSA Public Key: short exponent length");
+                }
+                e_bytes = (*p++) << 8;
+                e_bytes += *p++;
+                key_len -= 2;
+            }
+            if (key_len < e_bytes) {
+                isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+            }
+            if ((key_len - e_bytes) < 64) {
+                isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            if ((key_len - e_bytes) > 512) {
+                isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            RSA* rsa = RSA_new();
+            if (!rsa) {
+                throw std::bad_alloc();
+            }
+            rsa->e = BN_bin2bn(p, e_bytes, NULL);
+            p += e_bytes;
+            key_len -= e_bytes;
+            rsa->n = BN_bin2bn(p, key_len, NULL);
+            if (!rsa->e || !rsa->n) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if (kind_ == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // X.509 Public Key Certificate
+            const unsigned char* p =
+                reinterpret_cast<const unsigned char*>(key);
+            x509_ = d2i_X509(NULL, &p, static_cast<long>(key_len));
+            if (!x509_) {
+                isc_throw(BadKey, "d2i_X509");
+            }
+            int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+            if (hash_ == MD5) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.4")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA MD5 certificate");
+                }
+            } else if (hash_ == SHA1) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.5")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA1 certificate");
+                }
+            } else if (hash_ == SHA224) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.14")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA224 certificate");
+                }
+            } else if (hash_ == SHA256) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.11")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA256 certificate");
+                }
+            } else if (hash_ == SHA384) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.12")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA384 certificate");
+                }
+            } else if (hash_ == SHA512) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.13")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA512 certificate");
+                }
+            } else {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(UnsupportedAlgorithm,
+                          "Bad hash algorithm for certificate: " <<
+                          static_cast<int>(hash_));
+            }
+            pkey_ = X509_get_pubkey(x509_);
+            if (!pkey_) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "X509_get_pubkey");
+            }
+            if (pkey_->type != EVP_PKEY_RSA) {
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "not a RSA Public Key");
+            }
+        } else if (kind_ == CERT) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+
+        mdctx_.reset(new EVP_MD_CTX);
+        EVP_MD_CTX_init(mdctx_.get());
+
+        if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
+            EVP_MD_CTX_cleanup(mdctx_.get());
+            EVP_PKEY_free(pkey_);
+            pkey_ =NULL;
+            isc_throw(LibraryError, "EVP_DigestInit_ex");
+        }
+    }
+
+    /// @brief Constructor from a key file with password,
+    ///        asym and hash algorithm, key kind and key binary format
+    ///
+    /// See constructor of the @ref isc::cryptolink::Asym class for details.
+    ///
+    /// @param filename       The key file name/path
+    /// @param password       The PKCS#8 password
+    /// @param asym_algorithm The asymmetrical cryptography algorithm
+    /// @param hash_algorithm The hash algorithm
+    /// @param key_kind       The key kind
+    /// @param key_format     The key binary format
+    explicit AsymImpl(const std::string& filename,
+                      const std::string& password,
+                      const AsymAlgorithm asym_algorithm,
+                      const HashAlgorithm hash_algorithm,
+                      const AsymKeyKind key_kind,
+                      const AsymFormat key_format) {
+        algo_ = asym_algorithm;
+        hash_ = hash_algorithm;
+        kind_ = key_kind;
+        pkey_ = NULL;
+        x509_ = NULL;
+        if (algo_ != RSA_) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown asym algorithm: " <<
+                      static_cast<int>(algo_));
+        }
+        const EVP_MD* md = ossl::getHashAlgorithm(hash_);
+        if (md == 0) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown hash algorithm: " <<
+                      static_cast<int>(hash_));
+        }
+
+        if ((kind_ == PRIVATE) &&
+            ((key_format == BASIC) || (key_format == ASN1))) {
+            // PKCS#1 or PKCS#8 Private Key PEM file
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            char* pwd = NULL;
+            if (!password.empty()) {
+                pwd = const_cast<char*>(password.c_str());
+            }
+            pkey_ = PEM_read_PrivateKey(fp, NULL, 0, pwd);
+            fclose(fp);
+            if (!pkey_) {
+                isc_throw(BadKey, "PEM_read_PrivateKey");
+            }
+            if (pkey_->type != EVP_PKEY_RSA) {
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(BadKey, "not a RSA Private Key");
+            }
+        } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
+            // bind9 .private file
+            // warn when password not empty
+            if ((hash_ != MD5) && (hash_ != SHA1) &&
+                (hash_ != SHA256) && (hash_ != SHA512)) {
+                isc_throw(UnsupportedAlgorithm,
+                          "Not compatible hash algorithm: " <<
+                          static_cast<int>(hash_));
+            }
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            bool got_algorithm = false;
+            bool got_modulus = false;
+            bool got_pub_exponent = false;
+            bool got_priv_exponent = false;
+            bool got_prime1 = false;
+            bool got_prime2 = false;
+            bool got_exponent1 = false;
+            bool got_exponent2 = false;
+            bool got_coefficient = false;
+            RSA* rsa = RSA_new();
+            char line[4096];
+            while (fgets(line, sizeof(line), fp)) {
+                if (strncmp(line, "Algorithm:", strlen("Algorithm:")) == 0) {
+                    if (got_algorithm) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Algorithm entries");
+                    }
+                    got_algorithm = true;
+                    std::string value(line + strlen("Algorithm:") + 1);
+                    int alg = std::atoi(value.c_str());
+                    if (alg == 1) {
+                        // RSAMD5
+                        if (hash_ != MD5) {
+                            RSA_free(rsa);
+                            fclose(fp);
+                            isc_throw(BadKey, "Require a RSA MD5 key");
+                        }
+                    } else if ((alg == 5) || (alg == 7)) {
+                        // RSASHA1 or RSASHA1-NSEC3-SHA1
+                        if (hash_ != SHA1) {
+                            RSA_free(rsa);
+                            fclose(fp);
+                            isc_throw(BadKey, "Require a RSA SHA1 key");
+                        }
+                    } else if (alg == 8) {
+                        // RSASHA256
+                        if (hash_ != SHA256) {
+                            RSA_free(rsa);
+                            fclose(fp);
+                            isc_throw(BadKey, "Require a RSA SHA256 key");
+                        }
+                    } else if (alg == 10) {
+                        // RSASHA512
+                        if (hash_ != SHA512) {
+                            RSA_free(rsa);
+                            fclose(fp);
+                            isc_throw(BadKey, "Require a RSA SHA512 key");
+                        }
+                    } else {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Bad Algorithm: " << alg);
+                    }
+                } else if (strncmp(line, "Modulus:",
+                                   strlen("Modulus:")) == 0) {
+                    if (got_modulus) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Modulus entries");
+                    }
+                    got_modulus = true;
+                    std::string value(line + strlen("Modulus:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Modulus: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->n = BN_bin2bn(&bin[0], len, NULL);
+              } else if (strncmp(line, "PublicExponent:",
+                                 strlen("PublicExponent:")) == 0) {
+                    if (got_pub_exponent) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two PublicExponent entries");
+                    }
+                    got_pub_exponent = true;
+                    std::string value(line + strlen("PublicExponent:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "PublicExponent: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->e = BN_bin2bn(&bin[0], len, NULL);
+                } else if (strncmp(line, "PrivateExponent:",
+                                   strlen("PrivateExponent:")) == 0) {
+                    if (got_priv_exponent) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two PrivateExponent entries");
+                    }
+                    got_priv_exponent = true;
+                    std::string value(line + strlen("PrivateExponent:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "PrivateExponent: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->d = BN_bin2bn(&bin[0], len, NULL);
+                } else if (strncmp(line, "Prime1:", strlen("Prime1:")) == 0) {
+                    if (got_prime1) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Prime1 entries");
+                    }
+                    got_prime1 = true;
+                    std::string value(line + strlen("Prime1:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Prime1: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->p = BN_bin2bn(&bin[0], len, NULL);
+                } else if (strncmp(line, "Prime2:", strlen("Prime2:")) == 0) {
+                    if (got_prime2) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Prime2 entries");
+                    }
+                    got_prime2 = true;
+                    std::string value(line + strlen("Prime2:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Prime2: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->q = BN_bin2bn(&bin[0], len, NULL);
+                } else if (strncmp(line, "Exponent1:",
+                                   strlen("Exponent1:")) == 0) {
+                    if (got_exponent1) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Exponent1 entries");
+                    }
+                    got_exponent1 = true;
+                    std::string value(line + strlen("Exponent1:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Exponent1: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->dmp1 = BN_bin2bn(&bin[0], len, NULL);
+                } else if (strncmp(line, "Exponent2:",
+                                   strlen("Exponent2:")) == 0) {
+                    if (got_exponent2) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Exponent2 entries");
+                    }
+                    got_exponent2 = true;
+                    std::string value(line + strlen("Exponent2:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Exponent2: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->dmq1 = BN_bin2bn(&bin[0], len, NULL);
+                } else if (strncmp(line, "Coefficient:",
+                                   strlen("Coefficient:")) == 0) {
+                    if (got_coefficient) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Two Coefficient entries");
+                    }
+                    got_coefficient = true;
+                    std::string value(line + strlen("Coefficient:") + 1);
+                    std::vector<uint8_t> bin;
+                    try {
+                        isc::util::encode::decodeBase64(value, bin);
+                    } catch (const BadValue& exc) {
+                        RSA_free(rsa);
+                        fclose(fp);
+                        isc_throw(BadKey, "Coefficient: " << exc.what());
+                    }
+                    int len = static_cast<int>(bin.size());
+                    rsa->iqmp = BN_bin2bn(&bin[0], len, NULL);
+                }
+            }
+            fclose(fp);
+            if (!got_algorithm) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Algorithm entry");
+            }
+            if (!got_modulus) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Modulus entry");
+            }
+            if (!got_pub_exponent) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing PublicExponent entry");
+            }
+            if (!got_priv_exponent) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing PrivateExponent entry");
+            }
+            if (!got_prime1) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Prime1 entry");
+            }
+            if (!got_prime2) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Prime2 entry");
+            }
+            if (!got_exponent1) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Exponent1 entry");
+            }
+            if (!got_exponent2) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Exponent2 entry");
+            }
+            if (!got_coefficient) {
+                RSA_free(rsa);
+                isc_throw(BadKey, "Missing Coefficient entry");
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
+            // PKCS#1 PEM file
+            // warn when password not empty
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            RSA* rsa = PEM_read_RSAPublicKey(fp, NULL, 0, NULL);
+            fclose(fp);
+            if (!rsa) {
+                isc_throw(BadKey, "PEM_read_RSAPublicKey");
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo PEM file
+            // warn when password not empty
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            char* pwd = NULL;
+            if (!password.empty()) {
+                pwd = const_cast<char*>(password.c_str());
+            }
+            pkey_ = PEM_read_PUBKEY(fp, NULL, 0, pwd);
+            fclose(fp);
+            if (!pkey_) {
+                isc_throw(BadKey, "PEM_read_PUBKEY");
+            }
+            if (pkey_->type != EVP_PKEY_RSA) {
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(BadKey, "not a RSA Public Key");
+            }
+        } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+            // bind9 .key file (RDATA)
+            // warn when password not empty
+            if ((hash_ != MD5) && (hash_ != SHA1) &&
+                (hash_ != SHA256) && (hash_ != SHA512)) {
+                isc_throw(UnsupportedAlgorithm,
+                          "Not compatible hash algorithm: " <<
+                          static_cast<int>(hash_));
+            }
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            char line[4096];
+            bool found = false;
+            while (fgets(line, sizeof(line), fp)) {
+                if ((line[0] == '\0') || (line[0] == ';')) {
+                    continue;
+                }
+                if (strstr(line, "DNSKEY") == NULL) {
+                    continue;
+                }
+                found = true;
+                if (line[strlen(line) - 1] == '\n') {
+                    line[strlen(line) -1] = 0;
+                }
+                break;
+            }
+            fclose(fp);
+            if (!found) {
+                isc_throw(BadKey, "Can't find a DNSKEY");
+            }
+            const char b64[] =
+                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
+                "ghijklmnopqrstuvwxyz0123456789+/=";
+            size_t last = strlen(line) - 1;
+            while (strchr(b64, static_cast<int>(line[last])) != NULL) {
+                --last;
+            }
+            const std::string value(line + last + 1);
+            std::vector<uint8_t> bin;
+            try {
+                util::encode::decodeBase64(value, bin);
+            } catch (const BadValue& exc) {
+               isc_throw(BadKey, "Can't decode base64: " << exc.what());
+            }
+            size_t key_len = bin.size();
+            const uint8_t* p = &bin[0];
+            unsigned int e_bytes = *p++;
+            --key_len;
+            if (e_bytes == 0) {
+                if (key_len < 2) {
+                    isc_throw(BadKey,
+                              "Bad RSA Public Key: short exponent length");
+                }
+                e_bytes = (*p++) << 8;
+                e_bytes += *p++;
+                key_len -= 2;
+            }
+            if (key_len < e_bytes) {
+                isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+            }
+            if ((key_len - e_bytes) < 64) {
+                isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            if ((key_len - e_bytes) > 512) {
+                isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+                          (key_len - e_bytes) * 8);
+            }
+            RSA* rsa = RSA_new();
+            if (!rsa) {
+                throw std::bad_alloc();
+            }
+            rsa->e = BN_bin2bn(p, e_bytes, NULL);
+            p += e_bytes;
+            key_len -= e_bytes;
+            rsa->n = BN_bin2bn(p, key_len, NULL);
+            if (!rsa->e || !rsa->n) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            pkey_ = EVP_PKEY_new();
+            if (!pkey_) {
+                RSA_free(rsa);
+                throw std::bad_alloc();
+            }
+            if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+                RSA_free(rsa);
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+            }
+            // set1 bumped the reference counter
+            RSA_free(rsa);
+        } else if (kind_ == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // Public Key Certificate PEM file
+            // warn when password not empty
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            x509_ = PEM_read_X509(fp, NULL, 0, NULL);
+            fclose(fp);
+            if (!x509_) {
+                isc_throw(BadKey, "PEM_read_X509");
+            }
+            int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+            if (hash_ == MD5) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.4")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA MD5 certificate");
+                }
+            } else if (hash_ == SHA1) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.5")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA1 certificate");
+                }
+            } else if (hash_ == SHA224) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.14")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA224 certificate");
+                }
+            } else if (hash_ == SHA256) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.11")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA256 certificate");
+                }
+            } else if (hash_ == SHA384) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.12")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA384 certificate");
+                }
+            } else if (hash_ == SHA512) {
+                if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.13")) {
+                    X509_free(x509_);
+                    x509_ = NULL;
+                    isc_throw(BadKey, "Require a RSA SHA512 certificate");
+                }
+            } else {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(UnsupportedAlgorithm,
+                          "Bad hash algorithm for certificate: " <<
+                          static_cast<int>(hash_));
+            }
+            pkey_ = X509_get_pubkey(x509_);
+            if (!pkey_) {
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "X509_get_pubkey");
+            }
+            if (pkey_->type != EVP_PKEY_RSA) {
+                EVP_PKEY_free(pkey_);
+                pkey_ = NULL;
+                X509_free(x509_);
+                x509_ = NULL;
+                isc_throw(BadKey, "not a RSA Public Key");
+            }
+        } else if (kind_ == CERT) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Public Key Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+        mdctx_.reset(new EVP_MD_CTX);
+        EVP_MD_CTX_init(mdctx_.get());
+
+        if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
+            EVP_MD_CTX_cleanup(mdctx_.get());
+            EVP_PKEY_free(pkey_);
+            pkey_ =NULL;
+            isc_throw(LibraryError, "EVP_DigestInit_ex");
+        }
+    }
+
+    /// @brief Destructor
+    ~AsymImpl() {
+        if (mdctx_) {
+            EVP_MD_CTX_cleanup(mdctx_.get());
+        }
+        if (pkey_) {
+            EVP_PKEY_free(pkey_);
+            pkey_ = NULL;
+        }
+        if (x509_) {
+            X509_free(x509_);
+            x509_ = NULL;
+        }
+    }
+
+    /// @brief Returns the key size in bits
+    ///
+    size_t getKeySize() const {
+        return (static_cast<size_t>(EVP_PKEY_bits(pkey_)));
+    }
+
+    /// @brief Returns the output size of the signature
+    ///
+    /// \param sig_format The signature binary format
+    size_t getSignatureLength(const AsymFormat sig_format) const {
+        switch (algo_) {
+        case RSA_:
+            switch (sig_format) {
+            case BASIC:
+            case ASN1:
+            case DNS:
+                // In all cases a big integer of the size of n
+                return (static_cast<size_t>(EVP_PKEY_size(pkey_)));
+            default:
+                isc_throw(UnsupportedAlgorithm,
+                          "Unknown RSA Signature format: " <<
+                          static_cast<int>(sig_format));
+            }
+        default:
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown asym algorithm: " <<
+                      static_cast<int>(algo_));
+        }
+    }           
+
+    /// @brief Add data to digest
+    ///
+    /// See @ref isc::cryptolink::Asym::update() for details.
+    void update(const void* data, const size_t len) {
+        if (!EVP_DigestUpdate(mdctx_.get(), data, len)) {
+            isc_throw(LibraryError, "EVP_DigestUpdate");
+        }
+    }
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::Asym::sign() for details.
+    void sign(isc::util::OutputBuffer& result, size_t len,
+              const AsymFormat sig_format) {
+        unsigned int size = getSignatureLength(sig_format);
+        ossl::SecBuf<unsigned char> sig(size);
+        if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
+            isc_throw(LibraryError, "EVP_SignFinal");
+        }
+        if (len > size) {
+            len = size;
+        }
+        result.writeData(&sig[0], len);
+    }
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::Asym::sign() for details.
+    void sign(void* result, size_t len, const AsymFormat sig_format) {
+        unsigned int size = getSignatureLength(sig_format);
+        ossl::SecBuf<unsigned char> sig(size);
+        if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
+            isc_throw(LibraryError, "EVP_SignFinal");
+        }
+        if (len > size) {
+            len = size;
+        }
+        std::memcpy(result, &sig[0], len);
+    }
+
+    /// @brief Calculate the final signature
+    ///
+    /// See @ref isc::cryptolink::Asym::sign() for details.
+    std::vector<uint8_t> sign(size_t len, const AsymFormat sig_format) {
+        unsigned int size = getSignatureLength(sig_format);
+        ossl::SecBuf<unsigned char> sig(size);
+        if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
+            isc_throw(LibraryError, "EVP_SignFinal");
+        }
+        // resize to min(len, size)
+        sig.resize(len < size ? len : size);
+        return (std::vector<uint8_t>(sig.begin(), sig.end()));
+    }  
+
+    /// @brief Verify an existing signature
+    ///
+    /// See @ref isc::cryptolink::Asym::verify() for details.
+    bool verify(const void* sig, size_t len, const AsymFormat sig_format) {
+        size_t size = getSignatureLength(sig_format);
+        if (len != size) {
+            return false;
+        }
+        unsigned char* sigbuf =
+            reinterpret_cast<unsigned char*>(const_cast<void*>(sig));
+        unsigned int siglen = static_cast<unsigned int>(len);
+        int status = EVP_VerifyFinal(mdctx_.get(), sigbuf, siglen, pkey_);
+        switch (status) {
+        case 1:
+            return true;
+        case 0:
+            return false;
+        case -1:
+        default:
+            isc_throw(LibraryError, "EVP_VerifyFinal");
+        }
+    }
+
+    /// @brief Export the key value (binary)
+    ///
+    /// See @ref isc::cryptolink::Asym::exportkey() for details
+    std::vector<uint8_t> exportkey(const AsymKeyKind key_kind,
+                                   const AsymFormat key_format) const {
+        if ((key_kind == PRIVATE) && (key_format == BASIC)) {
+            // PKCS#1 Private Key
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+            }
+            int len = i2d_RSAPrivateKey(rsa, NULL);
+            if (len < 0) {
+                RSA_free(rsa);
+                isc_throw(LibraryError, "i2d_RSAPrivateKey 0");
+            }
+            std::vector<uint8_t> der(static_cast<size_t>(len));
+            unsigned char* p = &der[0];
+            len = i2d_RSAPrivateKey(rsa, &p);
+            RSA_free(rsa);
+            if (len != static_cast<int>(der.size())) {
+                isc_throw(LibraryError, "i2d_RSAPrivateKey");
+            }
+            return der;
+        } else if (key_kind == PRIVATE) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
+            // PKCS#1 Public Key
+            RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+            }
+            int len = i2d_RSAPublicKey(rsa, NULL);
+            if (len < 0) {
+                RSA_free(rsa);
+                isc_throw(LibraryError, "i2d_RSAPublicKey 0");
+            }
+            std::vector<uint8_t> der(static_cast<size_t>(len));
+            unsigned char* p = &der[0];
+            len = i2d_RSAPublicKey(rsa, &p);
+            RSA_free(rsa);
+            if (len != static_cast<int>(der.size())) {
+                isc_throw(LibraryError, "i2d_RSAPublicKey");
+            }
+            return der;
+        } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo
+            RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+            }
+            int len = i2d_RSA_PUBKEY(rsa, NULL);
+            if (len < 0) {
+                RSA_free(rsa);
+                isc_throw(LibraryError, "i2d_RSA_PUBKEY 0");
+            }
+            std::vector<uint8_t> der(static_cast<size_t>(len));
+            unsigned char* p = &der[0];
+            len = i2d_RSA_PUBKEY(rsa, &p);
+            RSA_free(rsa);
+            if (len != static_cast<int>(der.size())) {
+                isc_throw(LibraryError, "i2d_RSA_PUBKEY");
+            }
+            return der;
+        } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+            // RFC 3110 DNS wire format
+            RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+            }
+            size_t e_bytes = BN_num_bytes(rsa->e);
+            size_t mod_bytes = BN_num_bytes(rsa->n);
+            size_t x_bytes = 1;
+            if (e_bytes >= 256) {
+                x_bytes += 2;
+            }
+            std::vector<uint8_t> rdata(x_bytes + e_bytes + mod_bytes);
+            if (e_bytes < 256) {
+                rdata[0] = e_bytes;
+            } else {
+                rdata[0] = 0;
+                rdata[1] = (e_bytes >> 8) & 0xff;
+                rdata[2] = e_bytes & 0xff;
+            }
+            BN_bn2bin(rsa->e, &rdata[x_bytes]);
+            BN_bn2bin(rsa->n, &rdata[x_bytes + e_bytes]);
+            RSA_free(rsa);
+            return rdata;
+        } else if (kind_ == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // X.509 Public Key Certificate
+            if (kind_ != CERT) {
+                isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+            }
+            int len = i2d_X509(x509_, NULL);
+            if (len < 0) {
+                isc_throw(LibraryError, "i2d_X509 0");
+            }
+            std::vector<uint8_t> ber(static_cast<size_t>(len));
+            unsigned char* p = &ber[0];
+            len = i2d_X509(x509_, &p);
+            if (len != static_cast<int>(ber.size())) {
+                isc_throw(LibraryError, "i2d_X509");
+            }
+            return ber;
+        } else if (kind_ == CERT) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+    }
+
+    /// @brief Export the key value (file)
+    ///
+    /// See @ref isc::cryptolink::Asym::exportkey() for details
+    void exportkey(const std::string& filename,
+                   const std::string& password,
+                   const AsymKeyKind key_kind,
+                   const AsymFormat key_format) const {
+        if ((key_kind == PRIVATE) && (key_format == ASN1)) {
+            // PKCS#8 Private Key PEM file
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            char* pwd = NULL;
+            const EVP_CIPHER* enc = NULL;
+            if (!password.empty()) {
+                pwd = const_cast<char*>(password.c_str());
+                enc = EVP_des_ede3_cbc();
+                if (!enc) {
+                    isc_throw(LibraryError, "EVP_des_ede3_cbc");
+                }
+            }
+            FILE* fp = fopen(filename.c_str(), "w");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            if (!PEM_write_PKCS8PrivateKey(fp, pkey_, enc, pwd,
+                                           static_cast<int>(password.size()),
+                                           0, NULL)) {
+                fclose(fp);
+                isc_throw(LibraryError, "PEM_write_PKCS8PrivateKey");
+            }
+            fclose(fp);
+        } else if ((key_kind == PRIVATE) && (key_format == DNS)) {
+            //  bind9 .private file
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            if ((hash_ != MD5) && (hash_ != SHA1) &&
+                (hash_ != SHA256) && (hash_ != SHA512)) {
+                isc_throw(UnsupportedAlgorithm,
+                          "Not compatible hash algorithm: " <<
+                          static_cast<int>(hash_));
+            }
+            FILE* fp = fopen(filename.c_str(), "w");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                fclose(fp);
+                isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+            }
+            fprintf(fp, "Private-key-format: v1.2");
+            if (hash_ == MD5) {
+                fprintf(fp, "Algorithm: 1 (RSA)");
+            } else if (hash_ == SHA1) {
+                fprintf(fp, "Algorithm: 5 (RSASHA1)");
+            } else if (hash_ == SHA256) {
+                fprintf(fp, "Algorithm: 8 (RSASHA256)");
+            } else if (hash_ == SHA512) {
+                fprintf(fp, "Algorithm: 10 (RSASHA512)");
+            }
+            std::vector<uint8_t> bin;
+            bin.resize(BN_num_bytes(rsa->n));
+            BN_bn2bin(rsa->n, &bin[0]);
+            fprintf(fp, "Modulus: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->e));
+            BN_bn2bin(rsa->e, &bin[0]);
+            fprintf(fp, "PublicExponent: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->d));
+            BN_bn2bin(rsa->d, &bin[0]);
+            fprintf(fp, "PrivateExponent: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->p));
+            BN_bn2bin(rsa->p, &bin[0]);
+            fprintf(fp, "Prime1: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->q));
+            BN_bn2bin(rsa->q, &bin[0]);
+            fprintf(fp, "Prime2: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->dmp1));
+            BN_bn2bin(rsa->dmp1, &bin[0]);
+            fprintf(fp, "Exponent1: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->dmq1));
+            BN_bn2bin(rsa->dmq1, &bin[0]);
+            fprintf(fp, "Exponent2: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            bin.resize(BN_num_bytes(rsa->iqmp));
+            BN_bn2bin(rsa->iqmp, &bin[0]);
+            fprintf(fp, "Coefficient: %s",
+                    util::encode::encodeBase64(bin).c_str());
+            fclose(fp);
+            RSA_free(rsa);
+        } else if (key_kind == PRIVATE) {
+            if (kind_ != PRIVATE) {
+                isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+            }
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Private Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
+            // PKCS#1 PEM file
+            // warn when password not empty
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                fclose(fp);
+                isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+            }
+            if (!PEM_write_RSAPublicKey(fp, rsa)) {
+                fclose(fp);
+                RSA_free(rsa);
+                isc_throw(LibraryError, "PEM_write_RSAPublicKey");
+            }
+            fclose(fp);
+            RSA_free(rsa);
+        } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+            // SubjectPublicKeyInfo PEM file
+            // warn when password not empty
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            if (!PEM_write_PUBKEY(fp, pkey_)) {
+                fclose(fp);
+                isc_throw(LibraryError, "PEM_write_PUBKEY");
+            }
+            fclose(fp);
+        }  else if ((key_kind == PUBLIC) && (key_format == DNS)) {
+            // bind9 .key file (RDATA)
+            // warn when password not empty
+            std::vector<uint8_t> bin = exportkey(key_kind, key_format);
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            fprintf(fp, "; DNSKEY RDATA: %s\n",
+                    util::encode::encodeBase64(bin).c_str());
+            fclose(fp);
+        } else if (key_kind == PUBLIC) {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Public Key format: " <<
+                      static_cast<int>(key_format));
+        } else if ((kind_ == CERT) && (key_format == ASN1)) {
+            // Public Key Certificate PEM file
+            // warn when password not empty
+            if (!x509_) {
+                isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+            }
+            FILE* fp = fopen(filename.c_str(), "r");
+            if (!fp) {
+                isc_throw(BadKey, "Can't open file: " << filename);
+            }
+            if (!PEM_write_X509(fp, x509_)) {
+                fclose(fp);
+                isc_throw(LibraryError, "PEM_write_X509");
+            }
+            fclose(fp);
+        } else if (kind_ == CERT) {
+            if (!x509_) {
+                isc_throw(UnsupportedAlgorithm, "Have no Certificate");
+            }
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown Certificate format: " <<
+                      static_cast<int>(key_format));
+        } else {
+            isc_throw(UnsupportedAlgorithm,
+                      "Unknown RSA Key kind: " <<
+                      static_cast<int>(kind_));
+        }
+    }
+
+    /// @brief Check the validity
+    ///
+    /// See @ref isc::cryptolink::Asym::validate() for details
+    bool validate() const {
+        RSA* rsa;
+        X509_STORE* store;
+        X509_STORE_CTX* ctx;
+        int status;
+        switch (kind_) {
+        case PUBLIC:
+            // what to do?
+            return true;
+        case PRIVATE:
+            rsa = EVP_PKEY_get1_RSA(pkey_);
+            if (!rsa) {
+                return false;
+            }
+            status = RSA_check_key(rsa);
+            RSA_free(rsa);
+            return (status == 1);
+        case CERT:
+            store = X509_STORE_new();
+            if (!store) {
+                return false;
+            }
+            if (!X509_STORE_add_cert(store, x509_)) {
+                X509_STORE_free(store);
+                store = NULL;
+                return false;
+            }
+            ctx = X509_STORE_CTX_new();
+            if (!ctx) {
+                X509_STORE_free(store);
+                store = NULL;
+                return false;
+            }
+            if (!X509_STORE_CTX_init(ctx, store, x509_, NULL)) {
+                X509_STORE_CTX_free(ctx);
+                ctx = NULL;
+                X509_STORE_free(store);
+                store = NULL;
+                return false;
+            }
+            // By default OpenSSL skips self-signatures
+            X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CHECK_SS_SIGNATURE);
+            status = X509_verify_cert(ctx);
+            // text version of status available by
+            // X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))
+            X509_STORE_CTX_free(ctx);
+            ctx = NULL;
+            X509_STORE_free(store);
+            store = NULL;
+            if (status == 1) {
+                return true;
+            }
+            return false;
+        default:
+            return false;
+        }
+    }
+
+    /// @brief Compare two keys
+    ///
+    /// See @ref isc::cryptolink::Asym::compare() for details
+    bool compare(const AsymImpl* other, const AsymKeyKind key_kind) const {
+        int status;
+        switch (key_kind) {
+        case CERT:
+            // Special case for cert - cert
+            if ((kind_ == CERT) && (other->kind_ == CERT)) {
+                return (X509_cmp(x509_, other->x509_) == 0);
+            }
+            // At least one should be a cert
+            if ((kind_ != CERT) && (other->kind_ != CERT)) {
+                return false;
+            }
+            // For all other cases just compare public keys
+            // Falls into
+        case PUBLIC:
+            if ((kind_ != PUBLIC) &&
+                (kind_ != PRIVATE) &&
+                (kind_ != CERT)) {
+                return false;
+            }
+            if ((other->kind_ != PUBLIC) &&
+                (other->kind_ != PRIVATE) &&
+                (other->kind_ != CERT)) {
+                return false;
+            }
+            status = EVP_PKEY_cmp(pkey_, other->pkey_);
+            switch (status) {
+            case 1:
+                // match
+                return true;
+            case 0:
+                // don't match
+                return false;
+            case -1:
+                // different types
+                return false;
+            case -2:
+                // not supported
+                return false;
+            }
+            return false;
+        case PRIVATE:
+            if ((kind_ != PRIVATE) || (other->kind_ != PRIVATE)) {
+                return false;
+            }
+            // If public keys match so private too
+            status = EVP_PKEY_cmp(pkey_, other->pkey_);
+            switch (status) {
+            case 1:
+                // match
+                return true;
+            case 0:
+                // don't match
+                return false;
+            case -1:
+                // different types
+                return false;
+            case -2:
+                // not supported
+                return false;
+            }
+            return false;
+        default:
+            return false;
+        }
+    }
+
+private:
+    /// @brief The asymmetrical cryptography algorithm
+    AsymAlgorithm algo_;
+    /// @brief The hash algorithm
+    HashAlgorithm hash_;
+    /// @brief The key kind
+    AsymKeyKind kind_;
+    /// @brief The protected pointer to the OpenSSL EVP_MD_CTX structure
+    boost::scoped_ptr<EVP_MD_CTX> mdctx_;
+    /// @brief The raw pointer to the OpenSSL EVP_PKEY structure
+    /// There is no EVP_PKEY_init() or EVP_PKEY_cleanup() so
+    /// a smart pointer cannot be used.
+    EVP_PKEY* pkey_;
+    /// @brief The raw pointer to the OpenSSL X509 structure
+    /// There is no X509_init() or X509_cleanup() so
+    /// a smart pointer cannot be used.
+    X509* x509_;
+};
+
+Asym::Asym(const void* key, size_t key_len,
+           const AsymAlgorithm asym_algorithm,
+           const HashAlgorithm hash_algorithm,
+           const AsymKeyKind key_kind,
+           const AsymFormat key_format)
+{
+    impl_ = new AsymImpl(key, key_len,
+                         asym_algorithm, hash_algorithm,
+                         key_kind, key_format);
+}
+
+Asym::Asym(const std::vector<uint8_t> key,
+           const AsymAlgorithm asym_algorithm,
+           const HashAlgorithm hash_algorithm,
+           const AsymKeyKind key_kind,
+           const AsymFormat key_format)
+{
+    impl_ = new AsymImpl(&key[0], key.size(),
+                         asym_algorithm, hash_algorithm,
+                         key_kind, key_format);
+}
+
+Asym::Asym(const std::string& filename,
+           const std::string& password,
+           const AsymAlgorithm asym_algorithm,
+           const HashAlgorithm hash_algorithm,
+           const AsymKeyKind key_kind,
+           const AsymFormat key_format)
+{
+    impl_ = new AsymImpl(filename, password,
+                         asym_algorithm, hash_algorithm,
+                         key_kind, key_format);
+}
+
+Asym::~Asym() {
+    delete impl_;
+}
+
+size_t
+Asym::getKeySize() const {
+    return (impl_->getKeySize());
+}
+
+size_t
+Asym::getSignatureLength(const AsymFormat sig_format) const {
+    return (impl_->getSignatureLength(sig_format));
+}
+
+void
+Asym::update(const void* data, const size_t len) {
+    impl_->update(data, len);
+}
+
+void
+Asym::sign(isc::util::OutputBuffer& result, size_t len,
+           const AsymFormat sig_format) {
+    impl_->sign(result, len, sig_format);
+}
+
+void
+Asym::sign(void* result, size_t len, const AsymFormat sig_format) {
+    impl_->sign(result, len, sig_format);
+}
+
+std::vector<uint8_t>
+Asym::sign(size_t len, const AsymFormat sig_format) {
+    return (impl_->sign(len, sig_format));
+}
+
+bool
+Asym::verify(const void* sig, size_t len, const AsymFormat sig_format) {
+    return (impl_->verify(sig, len, sig_format));
+}
+
+std::vector<uint8_t>
+Asym::exportkey(const AsymKeyKind key_kind,
+                const AsymFormat key_format) const {
+    return (impl_->exportkey(key_kind, key_format));
+}
+
+void
+Asym::exportkey(const std::string& filename,
+                const std::string& password,
+                const AsymKeyKind key_kind,
+                const AsymFormat key_format) const {
+    impl_->exportkey(filename, password, key_kind, key_format);
+}
+
+bool
+Asym::validate() const {
+    return (impl_->validate());
+}
+
+bool
+Asym::compare(const Asym* other, const AsymKeyKind key_kind) const {
+    return (impl_->compare(other->impl_, key_kind));
+}
+
+} // namespace cryptolink
+} // namespace isc
index af47b0b30ee5290737d32a80c5e154b1dc22c5f4..1d68dad62f12f726e8fd4550be600ee81a74a01f 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <openssl/crypto.h>
 
+#include <openssl/evp.h>
+
 namespace isc {
 namespace cryptolink {
 
@@ -27,6 +29,7 @@ class CryptoLinkImpl {
 };
 
 CryptoLink::~CryptoLink() {
+    EVP_cleanup();
     delete impl_;
 }
 
@@ -36,6 +39,7 @@ CryptoLink::initialize() {
     if (c.impl_ == NULL) {
         try {
             c.impl_ = new CryptoLinkImpl();
+            OpenSSL_add_all_algorithms();
         } catch (const std::exception &ex) {
             // Should never happen
             isc_throw(InitializationError,
index e5ad4417108b635ec9a116bef9b057e7d7cb0e61..572336824bd3598e4b075f2fc81b04b608244647 100644 (file)
@@ -2,6 +2,8 @@ SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(abs_top_srcdir)/src/lib/cryptolink/tests/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/cryptolink/tests/testdata\"
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
 
 if USE_STATIC_LINK
@@ -20,6 +22,7 @@ run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += crypto_unittests.cc
 run_unittests_SOURCES += hash_unittests.cc
 run_unittests_SOURCES += hmac_unittests.cc
+run_unittests_SOURCES += asym_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS =  $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD) $(CRYPTO_LIBS)
@@ -30,3 +33,5 @@ run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
+
+#EXTRA_DIST += testdata/xxx
diff --git a/src/lib/cryptolink/tests/asym_unittests.cc b/src/lib/cryptolink/tests/asym_unittests.cc
new file mode 100644 (file)
index 0000000..4de76fd
--- /dev/null
@@ -0,0 +1,806 @@
+// Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <stdlib.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <util/encode/base64.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+using boost::lexical_cast;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::cryptolink;
+
+namespace {
+
+#define MODULUS        \
+0xb3, 0x25, 0xc2, 0x01, 0xee, 0x8c, 0x91, 0x5d, \
+0xa3, 0xea, 0x55, 0x90, 0xa2, 0x2c, 0xb3, 0xb1, \
+0x35, 0x18, 0x7b, 0xa1, 0x00, 0x80, 0x4f, 0x21, \
+0x32, 0x94, 0xd1, 0xa2, 0xc3, 0x1a, 0xbc, 0x0a, \
+0xee, 0xcd, 0x7f, 0x23, 0x12, 0x5d, 0xf8, 0x92, \
+0xcb, 0x87, 0x69, 0xfd, 0x85, 0x0f, 0x25, 0x57, \
+0x88, 0x69, 0x4b, 0x5e, 0x93, 0x12, 0x3a, 0x93, \
+0xa9, 0x23, 0x64, 0x0f, 0x7c, 0xaa, 0x20, 0xca, \
+0x56, 0x93, 0x81, 0xe9, 0x64, 0xb2, 0x20, 0xad, \
+0x55, 0x25, 0x87, 0xb1, 0x85, 0xc8, 0xd6, 0xfe, \
+0x5f, 0xa9, 0xcd, 0x53, 0x2e, 0xaf, 0x9e, 0x53, \
+0xa4, 0xfb, 0xba, 0xef, 0x30, 0x53, 0x9d, 0x01, \
+0xee, 0xb7, 0xff, 0xaa, 0xde, 0x7a, 0xfa, 0x57, \
+0xfb, 0xdf, 0x63, 0xf3, 0x9c, 0x77, 0x2e, 0xa4, \
+0x97, 0x10, 0x30, 0xc1, 0x15, 0x48, 0x6b, 0x01, \
+0x1e, 0x57, 0x8d, 0x5d, 0xeb, 0xfc, 0x42, 0xe1
+
+#define PUBLIC_EXPONENT 0x01, 0x00, 0x01
+
+#define PUBLIC_KEY \
+0x30, 0x81, 0x89, \
+0x02, 0x81, 0x81, 0x00, MODULUS, \
+0x02, 0x03, PUBLIC_EXPONENT
+
+#define PRIVATE_EXPONENT \
+0xa8, 0xc9, 0x93, 0x5f, 0xe4, 0x94, 0xf6, 0x45, \
+0x26, 0xb2, 0x1b, 0x8a, 0x18, 0xf2, 0x4b, 0x1f, \
+0x54, 0x2a, 0x4c, 0x18, 0xe6, 0x72, 0xfd, 0x9b, \
+0x06, 0xa0, 0x26, 0x5f, 0xd6, 0xb9, 0x32, 0xa0, \
+0x8e, 0x5c, 0x79, 0x43, 0xdf, 0x03, 0x40, 0xb7, \
+0x76, 0x21, 0x90, 0xa0, 0x37, 0x24, 0x8d, 0x07, \
+0x4a, 0xd4, 0x02, 0x1a, 0x0a, 0x31, 0x6b, 0x95, \
+0x42, 0x0f, 0xc1, 0x2f, 0xc2, 0x42, 0x3c, 0x7f, \
+0x33, 0xb3, 0x54, 0x2f, 0x83, 0xf8, 0x5d, 0x7f, \
+0x86, 0xa5, 0xe8, 0xbe, 0xae, 0x40, 0x41, 0x20, \
+0x5d, 0xa2, 0x66, 0x9a, 0x82, 0x66, 0x03, 0xfc, \
+0xf0, 0x62, 0x24, 0x08, 0x25, 0x99, 0xc6, 0x2b, \
+0xef, 0x9d, 0x79, 0x54, 0x21, 0x91, 0x62, 0x76, \
+0xe5, 0x0b, 0x30, 0xe7, 0x96, 0x9c, 0xfa, 0x45, \
+0xf1, 0x6f, 0xa7, 0x6b, 0x94, 0xa2, 0x70, 0x3d, \
+0xdd, 0x74, 0x6d, 0x4c, 0x8e, 0x8e, 0xe7, 0x01
+
+#define PRIME1 \
+0xda, 0x36, 0xde, 0xd3, 0x0a, 0x15, 0x20, 0xac, \
+0x79, 0xa7, 0xe0, 0xa6, 0x55, 0x69, 0xea, 0xcd, \
+0xf7, 0xe7, 0xf3, 0xda, 0xf6, 0xeb, 0xdc, 0xdb, \
+0x7c, 0xdd, 0x13, 0x6c, 0xaa, 0xfd, 0x6e, 0x98, \
+0x7b, 0x54, 0x25, 0xc0, 0x3b, 0x7d, 0xf9, 0xb5, \
+0x5b, 0x97, 0x2f, 0xe6, 0xc1, 0x4d, 0x7d, 0x5e, \
+0x3b, 0x51, 0x9c, 0xc6, 0xb3, 0xd4, 0x23, 0x43, \
+0xbf, 0x42, 0x98, 0x50, 0x03, 0xfb, 0x0b, 0x51
+
+#define PRIME2 \
+0xd2, 0x2b, 0x1c, 0x29, 0x83, 0x01, 0xfd, 0x41, \
+0x49, 0xfb, 0x95, 0x89, 0x7c, 0xaf, 0x81, 0x81, \
+0xce, 0xd1, 0x77, 0x5f, 0x65, 0x61, 0x61, 0x07, \
+0xd5, 0x45, 0x58, 0xc5, 0xdd, 0x73, 0x6b, 0x63, \
+0x3a, 0x82, 0x92, 0xad, 0xa6, 0x71, 0xa1, 0xcf, \
+0xe3, 0xb5, 0x10, 0x52, 0x42, 0xe5, 0x76, 0x8a, \
+0x1b, 0xca, 0x6a, 0x52, 0x9d, 0x54, 0x78, 0x07, \
+0xcf, 0x1e, 0xed, 0xe4, 0xf2, 0xf2, 0xba, 0x91
+
+#define EXPONENT1 \
+0x6e, 0xf6, 0xa4, 0x0c, 0x90, 0xfd, 0xf9, 0x65, \
+0x7b, 0x5f, 0xa0, 0xdf, 0x34, 0x63, 0xed, 0xe0, \
+0xdb, 0x05, 0x7a, 0x7d, 0x88, 0x3e, 0x9c, 0x4a, \
+0x88, 0x8e, 0x2b, 0x08, 0x81, 0x52, 0xea, 0x60, \
+0x63, 0xa6, 0x80, 0xa3, 0xe6, 0x1c, 0xc3, 0x54, \
+0x33, 0xc5, 0x07, 0xb8, 0xc1, 0xe7, 0x53, 0xaf, \
+0x0d, 0x5f, 0x0c, 0xe8, 0x06, 0x1e, 0x03, 0xe8, \
+0xb9, 0x63, 0x75, 0xec, 0x8a, 0x79, 0xa8, 0x61
+
+#define EXPONENT2 \
+0x7c, 0x6d, 0x29, 0x6f, 0x2a, 0x30, 0xb3, 0x4f, \
+0x44, 0x0d, 0xbe, 0xaa, 0x77, 0x37, 0x30, 0xe5, \
+0x39, 0x1c, 0xaa, 0x1f, 0xc0, 0x55, 0xb6, 0xac, \
+0x7c, 0x87, 0x61, 0xd7, 0x43, 0x14, 0x62, 0x2f, \
+0x8d, 0x24, 0x60, 0xd2, 0x8e, 0x08, 0x18, 0x54, \
+0x8b, 0xeb, 0x56, 0x8e, 0x5d, 0x2c, 0x9c, 0xd1, \
+0x87, 0x42, 0x7f, 0x50, 0x09, 0xf5, 0x48, 0x5a, \
+0xd7, 0x34, 0xe8, 0x82, 0xf3, 0x92, 0xe1, 0x01
+
+#define COEFFICIENT \
+0x13, 0x5f, 0x97, 0x80, 0x10, 0x66, 0x54, 0x05, \
+0x55, 0x05, 0x03, 0x37, 0x96, 0xe0, 0xc0, 0xa7, \
+0x25, 0x7b, 0x70, 0x4a, 0xd2, 0x0e, 0xce, 0x82, \
+0x42, 0x3e, 0xb9, 0x5a, 0x2d, 0x87, 0xca, 0xfa, \
+0xaa, 0x82, 0xe4, 0xed, 0xdb, 0xbf, 0xbf, 0x26, \
+0x9b, 0x50, 0x84, 0x9e, 0xb0, 0xd0, 0x10, 0xb4, \
+0x79, 0x56, 0x1c, 0xd1, 0x49, 0xa1, 0x4f, 0xf8, \
+0x52, 0x6e, 0xb4, 0x1b, 0x1a, 0x5f, 0x48, 0x0c
+
+#define PRIVATE_KEY \
+0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, \
+0x02, 0x81, 0x81, 0x00, MODULUS, \
+0x02, 0x03, PUBLIC_EXPONENT, \
+0x02, 0x81, 0x81, 0x00, PRIVATE_EXPONENT, \
+0x02, 0x41, 0x00, PRIME1, \
+0x02, 0x41, 0x00, PRIME2, \
+0x02, 0x40, EXPONENT1, \
+0x02, 0x40, EXPONENT2, \
+0x02, 0x40, COEFFICIENT
+
+    /// @brief Public Key in PKCS#1 format
+    const uint8_t pubpkcs1[] = {
+        0x30, // tag=SEQUENCE (RSAPublicKey)
+        0x81, 0x89, // length=137
+         0x02, // tag=INTEGER (modulus -- n)
+         0x81, 0x81, // length=129
+          0x00, MODULUS,
+         0x02, // tag=INTEGER (publicExponent -- e)
+         0x03, // length=3
+          PUBLIC_EXPONENT
+    };
+    size_t pubpkcs1len = 140;
+
+    /// @brief Public Key in SubjectPublicKeyInfo format
+    const uint8_t pubspki[] = {
+        0x30, // tag=SEQUENCE (SubjectPublicKeyInfo)
+        0x81, 0x9f, // length=159
+         0x30, // tag=SEQUENCE (algorithm : AlgorithmIdentifier)
+         0x0d, // length=13
+          0x06, // tag=OBJECTID (algorithm)
+          0x09, // length=9
+           0x2a, 0x86, 0x48, 0x86,
+           0xf7, 0x0d, 0x01, 0x01,
+           0x01, // value=rsaEncryption
+          0x05, // tag=NULL (parameter)
+          0x00, // length=00
+         0x03, // tag=BIT STRING (subjectPublicKey)
+        0x81, 0x8d, // length=141
+         0x00, PUBLIC_KEY
+    };
+    size_t pubspkilen = 162;
+
+    /// @brief Public Key in DNSSEC format (length 132)
+    const uint8_t pubdns[] = {
+        0x03, // length=3
+        PUBLIC_EXPONENT,
+        MODULUS
+    };
+    size_t pubdnslen = 132;
+
+    /// @brief Private Key in PKCS#1 format
+    const uint8_t privpkcs1[] = {
+        0x30, // tag=SEQUENCE (RSAPrivateKey)
+        0x82, 0x02, 0x5c, // length=604
+         0x02, // tag=INTEGER (version : Version)
+         0x01, // length=1
+          0x00, // value=0
+         0x02, // tag=INTEGER (modulus -- n)
+         0x81, 0x81, // length=129
+          0x00, MODULUS,
+         0x02, // tag=INTEGER (publicExponent -- e)
+         0x03, // length=3
+          PUBLIC_EXPONENT,
+         0x02, // tag=INTEGER (privateExponent -- d)
+         0x81, 0x81, // length=129
+          0x00, PRIVATE_EXPONENT,
+         0x02, // tag=INTEGER (prime1 -- p)
+         0x41, // length=65
+          0x00, PRIME1,
+         0x02, // tag=INTEGER (prime2 -- q)
+         0x41, // length=65
+          0x00, PRIME2,
+         0x02, // tag=INTEGER (exponent1 -- d mod (p-1))
+         0x40, // length=64
+          EXPONENT1,
+         0x02, // tag=INTEGER (exponent2 -- d mod (q-1))
+         0x40, // length=64
+          EXPONENT2,
+         0x02, // tag=INTEGER (coefficient -- 1/q mod p)
+         0x40, // length=64
+          COEFFICIENT
+    };
+    size_t privpkcs1len = 608;
+
+    /// @brief Public Key file (SubjectPublicKeyInfo in PEM)
+    const std::string pubfile = TEST_DATA_SRCDIR "/pub.pem";
+
+    /// @brief Private Key file (PKCS#8 in PEM, password '1234')
+    const std::string privfile = TEST_DATA_SRCDIR "/pkcs8.pem";
+
+    /// @brief Certificate file (X.509 Public Key Certificate in PEM)
+    const std::string certfile = TEST_DATA_SRCDIR "/x509.pem";
+
+    /// @brief Compare data with expected value
+    /// @param data Value to compare
+    /// @param expected Expected value
+    /// @param len Length of the expected value
+    void checkData(const uint8_t* data, const uint8_t* expected,
+                   size_t len) {
+        for (size_t i = 0; i < len; ++i) {
+            ASSERT_EQ(expected[i], data[i]);
+        }
+    }
+
+    /// @brief Compare OutputBuffer with expected value
+    /// encapsulated checkData()
+    /// @param buf buffer to compare
+    /// @param expected Expected value
+    /// @param len Length of the expected value
+    void checkBuffer(const OutputBuffer& buf, const uint8_t* expected,
+                     size_t len)
+    {
+        ASSERT_EQ(len, buf.getLength());
+        checkData(static_cast<const uint8_t*>(buf.getData()), expected,
+                  len);
+    }
+
+    /// @brief Sign and verify with an instantiation of an asymmetrical
+    /// cryptography object
+    /// See @ref doAsymTest for parameters
+    void doAsymTestDirect(const std::string& data,
+                          const std::string& privfilename,
+                          const std::string& password,
+                          const std::string& pubfilename,
+                          const AsymAlgorithm asym_algorithm,
+                          const HashAlgorithm hash_algorithm,
+                          const AsymFormat sig_format,
+                          const uint8_t* expected_sig,
+                          size_t sig_len) {
+        OutputBuffer data_buf(data.size());
+        data_buf.writeData(data.c_str(), data.size());
+        OutputBuffer sig(1);
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+        // Sign it
+        boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfilename,
+                                                           password,
+                                                           asym_algorithm,
+                                                           hash_algorithm,
+                                                           PRIVATE,
+                                                           ASN1),
+                                         deleteAsym);
+        rsa_sign->update(data_buf.getData(), data_buf.getLength());
+        rsa_sign->sign(sig, sig_len, sig_format);
+
+        // Check if the signature is what we expect
+        checkBuffer(sig, expected_sig, sig_len);
+
+        // Check whether we can verify it ourselves
+        boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfilename,
+                                                             "",
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PUBLIC,
+                                                             ASN1),
+                                           deleteAsym);
+        rsa_verify->update(data_buf.getData(), data_buf.getLength());
+        EXPECT_TRUE(rsa_verify->verify(sig.getData(),
+                                       sig.getLength(),
+                                       sig_format));
+
+        // Change the length and check whether verification fails then
+        // Relies on the fact the length is checked first
+        EXPECT_FALSE(rsa_verify->verify(sig.getData(),
+                                        sig.getLength() - 1,
+                                        sig_format));
+        EXPECT_FALSE(rsa_verify->verify(sig.getData(),
+                                        sig.getLength() + 1,
+                                        sig_format));
+
+        // Change the sig by flipping the first octet, and check
+        // whether verification fails then
+        sig.writeUint8At(~sig[0], 0);
+        EXPECT_FALSE(rsa_verify->verify(sig.getData(),
+                                        sig.getLength(),
+                                        sig_format));
+    }
+
+    /// @brief Sign and verify with vector representation of signature
+    /// cryptography object
+    /// See @ref doAsymTest for parameters
+    void doAsymTestVector(const std::string& data,
+                          const std::string& privfilename,
+                          const std::string& password,
+                          const std::string& pubfilename,
+                          const AsymAlgorithm asym_algorithm,
+                          const HashAlgorithm hash_algorithm,
+                          const AsymFormat sig_format,
+                          const uint8_t* expected_sig,
+                          size_t sig_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfilename,
+                                                           password,
+                                                           asym_algorithm,
+                                                           hash_algorithm,
+                                                           PRIVATE,
+                                                           ASN1),
+                                         deleteAsym);
+        rsa_sign->update(data.c_str(), data.size());
+        std::vector<uint8_t> sig = rsa_sign->sign(sig_len, sig_format);
+        ASSERT_EQ(sig_len, sig.size());
+        checkData(&sig[0], expected_sig, sig_len);
+
+        boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfilename,
+                                                             "",
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PUBLIC,
+                                                             ASN1),
+                                           deleteAsym);
+        rsa_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(rsa_verify->verify(&sig[0], sig.size(), sig_format));
+
+        EXPECT_FALSE(rsa_verify->verify(&sig[0], sig.size() - 1, sig_format));
+        EXPECT_FALSE(rsa_verify->verify(&sig[0], sig.size() + 1, sig_format));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(rsa_verify->verify(&sig[0], sig.size(), sig_format));
+    }
+
+    /// @brief Sign and verify with array representation of signature
+    /// cryptography object
+    /// See @ref doAsymTest for parameters
+    void doAsymTestArray(const std::string& data,
+                         const std::string& privfilename,
+                         const std::string& password,
+                         const std::string& pubfilename,
+                         const AsymAlgorithm asym_algorithm,
+                         const HashAlgorithm hash_algorithm,
+                         const AsymFormat sig_format,
+                         const uint8_t* expected_sig,
+                          size_t sig_len) {
+        CryptoLink& crypto = CryptoLink::getCryptoLink();
+        boost::shared_ptr<Asym> rsa_sign(crypto.createAsym(privfilename,
+                                                           password,
+                                                           asym_algorithm,
+                                                           hash_algorithm,
+                                                           PRIVATE,
+                                                           ASN1),
+                                         deleteAsym);
+        rsa_sign->update(data.c_str(), data.size());
+
+        // note: this is not exception-safe, and can leak, but
+        // if there is an unexpected exception in the code below we
+        // have more important things to fix.
+        uint8_t* sig = new uint8_t[sig_len];
+
+        rsa_sign->sign(sig, sig_len, sig_format);
+        checkData(sig, expected_sig, sig_len);
+
+        boost::shared_ptr<Asym> rsa_verify(crypto.createAsym(pubfilename,
+                                                             "",
+                                                             asym_algorithm,
+                                                             hash_algorithm,
+                                                             PUBLIC,
+                                                             ASN1),
+                                           deleteAsym);
+        rsa_verify->update(data.c_str(), data.size());
+        EXPECT_TRUE(rsa_verify->verify(sig, sig_len, sig_format));
+
+        EXPECT_FALSE(rsa_verify->verify(sig, sig_len - 1, sig_format));
+        EXPECT_FALSE(rsa_verify->verify(sig, sig_len + 1, sig_format));
+
+        sig[0] = ~sig[0];
+        EXPECT_FALSE(rsa_verify->verify(sig, sig_len, sig_format));
+
+        delete[] sig;
+    }
+
+    /// @brief Sign and verify using all variants
+    /// @param data Input value
+    /// @param privfilename Private key file name
+    /// @param password Private key password
+    /// @param pubfilename Public key file name
+    /// @param asym_algorithm Asym algorithm enum
+    /// @param hash_algorithm Hash algorithm enum
+    /// @param sig_format Signature format enum
+    /// @param expected_sig Expected value
+    /// @param sig_len Expected value length
+    void doAsymTest(const std::string& data,
+                    const std::string& privfilename,
+                    const std::string& password,
+                    const std::string& pubfilename,
+                    const AsymAlgorithm asym_algorithm,
+                    const HashAlgorithm hash_algorithm,
+                    const AsymFormat sig_format,
+                    const uint8_t* expected_sig,
+                    size_t sig_len) {
+        doAsymTestDirect(data, privfilename, password, pubfilename,
+                         asym_algorithm, hash_algorithm, sig_format,
+                         expected_sig, sig_len);
+        doAsymTestVector(data, privfilename, password, pubfilename,
+                         asym_algorithm, hash_algorithm, sig_format,
+                         expected_sig, sig_len);
+        doAsymTestArray(data, privfilename, password, pubfilename,
+                        asym_algorithm, hash_algorithm, sig_format,
+                        expected_sig, sig_len);
+    }
+}
+
+//
+// Test values
+//
+TEST(AsymTest, RSA_SHA1) {
+    const uint8_t sig_expected[] = {
+        0x16, 0xaa, 0xd3, 0x27, 0x5b, 0x22, 0xff, 0x2b,
+        0x74, 0x77, 0x7a, 0x20, 0x6c, 0xdc, 0xa6, 0xb1,
+        0x88, 0x1a, 0xb3, 0xc6, 0x5a, 0xae, 0x35, 0x3a,
+        0x04, 0x8d, 0x7f, 0x81, 0x5b, 0xef, 0xd6, 0xe2,
+        0x07, 0xee, 0xec, 0x1e, 0xf4, 0x89, 0x82, 0x6c,
+        0x2c, 0x0c, 0x25, 0x8a, 0xf0, 0x8a, 0xde, 0x6c,
+        0xf7, 0x66, 0x9d, 0xa6, 0xd5, 0x69, 0x1e, 0x47,
+        0x76, 0xf3, 0xe7, 0x47, 0x12, 0xd5, 0x92, 0x45,
+        0xb5, 0xc6, 0x50, 0x32, 0xe1, 0x25, 0xd9, 0xa1,
+        0x23, 0xd1, 0x12, 0x6d, 0x1f, 0xa8, 0x9e, 0xc3,
+        0xdb, 0x41, 0xb3, 0x13, 0x0b, 0x7a, 0xea, 0x72,
+        0xa7, 0x60, 0xc8, 0xfd, 0x89, 0xee, 0x36, 0xe4,
+        0x5c, 0xa8, 0xfa, 0x86, 0x4b, 0xcc, 0x15, 0x7d,
+        0xcb, 0x79, 0x90, 0x51, 0xa2, 0x62, 0x43, 0xc7,
+        0xe1, 0x04, 0x08, 0x13, 0x68, 0x6d, 0x7b, 0x4d,
+        0x45, 0xca, 0x3e, 0x6c, 0xba, 0x62, 0x90, 0x0a,
+        // pad
+        0x00 
+    };
+    const size_t sig_len = 128;
+    doAsymTest("Permission to use, copy, modify, and/or "
+               "distribute this software\n",
+               privfile, "1234", pubfile, RSA_, SHA1, BASIC,
+               sig_expected, sig_len);
+}
+
+//
+// Constructor
+//
+TEST(AsymTest, BAD_ALGO) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    EXPECT_THROW(crypto.createAsym(pubpkcs1, pubpkcs1len,
+                                   DSA_, SHA1, PUBLIC, BASIC),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(pubpkcs1, pubpkcs1len,
+                                   ECDSA_, SHA1, PUBLIC, BASIC),
+                 UnsupportedAlgorithm);
+
+    EXPECT_THROW(crypto.createAsym(pubfile, "", DSA_, SHA1, PUBLIC, ASN1),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(pubfile, "", ECDSA_, SHA1, PUBLIC, ASN1),
+                 UnsupportedAlgorithm);
+
+    EXPECT_THROW(crypto.createAsym(pubpkcs1, pubpkcs1len,
+                                   RSA_, UNKNOWN_HASH, PUBLIC, BASIC),
+                 UnsupportedAlgorithm);
+
+    EXPECT_THROW(crypto.createAsym(pubfile, "", RSA_, UNKNOWN_HASH,
+                                   PUBLIC, ASN1),
+                 UnsupportedAlgorithm);
+}
+
+TEST(AsymTest, RSA_PUB_PKCS1) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubpkcs1, pubpkcs1len,
+                                                      RSA_, SHA1,
+                                                      PUBLIC, BASIC),
+                                    deleteAsym);
+    ASSERT_TRUE(pub_key.get()->validate());
+    EXPECT_EQ(1024, pub_key.get()->getKeySize());
+    EXPECT_EQ(128, pub_key.get()->getSignatureLength(BASIC));
+    EXPECT_EQ(128, pub_key.get()->getSignatureLength(ASN1));
+    EXPECT_EQ(128, pub_key.get()->getSignatureLength(DNS));
+
+    EXPECT_THROW(crypto.createAsym(pubpkcs1, pubpkcs1len - 1,
+                                   RSA_, SHA1, PUBLIC, BASIC),
+                 BadKey);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(pub_key.get()->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key.get()->compare(pub_key.get(), PUBLIC));
+    EXPECT_FALSE(pub_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key.get()->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> pubbin =
+        ref_key.get()->exportkey(PUBLIC, BASIC);
+    checkData(&pubbin[0], pubpkcs1, pubpkcs1len);
+}
+
+TEST(AsymTest, RSA_PUB_SPKI) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubspki, pubspkilen,
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    ASSERT_TRUE(pub_key.get()->validate());
+    EXPECT_EQ(1024, pub_key.get()->getKeySize());
+
+    EXPECT_THROW(crypto.createAsym(pubspki, pubspkilen - 1,
+                                   RSA_, SHA1, PUBLIC, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(pubspki, pubspkilen,
+                                   RSA_, SHA1, PUBLIC, BASIC),
+                 BadKey);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(pub_key.get()->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key.get()->compare(pub_key.get(), PUBLIC));
+    EXPECT_FALSE(pub_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key.get()->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> pubbin =
+        ref_key.get()->exportkey(PUBLIC, ASN1);
+    checkData(&pubbin[0], pubspki, pubspkilen);
+}
+
+TEST(AsymTest, RSA_PUB_DNS) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubdns, pubdnslen,
+                                                      RSA_, SHA1,
+                                                      PUBLIC, DNS),
+                                    deleteAsym);
+    ASSERT_TRUE(pub_key.get()->validate());
+    EXPECT_EQ(1024, pub_key.get()->getKeySize());
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(pub_key.get()->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key.get()->compare(pub_key.get(), PUBLIC));
+    EXPECT_FALSE(pub_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key.get()->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> pubbin =
+        ref_key.get()->exportkey(PUBLIC, DNS);
+    EXPECT_EQ(pubbin.size(), pubdnslen);
+    checkData(&pubbin[0], pubdns, pubdnslen);
+
+    const std::string keystr =
+       "AwEAAbMlwgHujJFdo+pVkKIss7E1GHuhAIBPITKU0aLDGrwK7s1/IxJd+JLLh2n"
+       "9hQ8lV4hpS16TEjqTqSNkD3yqIMpWk4HpZLIgrVUlh7GFyNb+X6nNUy6vnlOk+7r"
+       "vMFOdAe63/6reevpX+99j85x3LqSXEDDBFUhrAR5XjV3r/ELh";
+    std::vector<uint8_t> keybin;
+    decodeBase64(keystr, keybin);
+    EXPECT_EQ(keybin.size(), pubdnslen);
+    checkData(&keybin[0], pubdns, pubdnslen);
+
+    const std::string dnsfile = TEST_DATA_SRCDIR "/Kexample.+005+18330.key";
+    boost::shared_ptr<Asym> dns_key(crypto.createAsym(dnsfile, "",
+                                                     RSA_, SHA1,
+                                                     PUBLIC, DNS),
+                                   deleteAsym);
+    EXPECT_TRUE(dns_key.get()->compare(ref_key.get(), PUBLIC));
+    EXPECT_TRUE(ref_key.get()->compare(dns_key.get(), PUBLIC));
+}
+
+TEST(AsymTest, RSA_PRIV_PKCS1) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> priv_key(crypto.createAsym(privpkcs1, privpkcs1len,
+                                                      RSA_, SHA1,
+                                                      PRIVATE, BASIC),
+                                    deleteAsym);
+    ASSERT_TRUE(priv_key.get()->validate());
+    EXPECT_EQ(1024, priv_key.get()->getKeySize());
+    EXPECT_EQ(128, priv_key.get()->getSignatureLength(BASIC));
+    EXPECT_EQ(128, priv_key.get()->getSignatureLength(ASN1));
+    EXPECT_EQ(128, priv_key.get()->getSignatureLength(DNS));
+
+    EXPECT_THROW(crypto.createAsym(privpkcs1, privpkcs1len - 1,
+                                   RSA_, SHA1, PRIVATE, BASIC),
+                 BadKey);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(privfile, "1234",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(priv_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key.get()->compare(priv_key.get(), PRIVATE));
+    EXPECT_TRUE(priv_key.get()->compare(ref_key.get(), PUBLIC));
+    EXPECT_FALSE(priv_key.get()->compare(ref_key.get(), CERT));
+
+    const std::vector<uint8_t> privbin =
+        ref_key.get()->exportkey(PRIVATE, BASIC);
+    checkData(&privbin[0], privpkcs1, privpkcs1len);
+
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(priv_key.get()->compare(pub_key.get(), PUBLIC));
+    EXPECT_TRUE(pub_key.get()->compare(priv_key.get(), PUBLIC));
+}
+
+TEST(AsymTest, RSA_PRIV_PKCS) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(privfile, "1234",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, ASN1),
+                                    deleteAsym);
+#ifndef WITH_BOTAN
+    const std::string pkcs1file = TEST_DATA_SRCDIR "/priv.pem";
+    boost::shared_ptr<Asym> pkcs1_key(crypto.createAsym(pkcs1file, "",
+                                                        RSA_, SHA1,
+                                                        PRIVATE, BASIC),
+                                    deleteAsym);
+    EXPECT_TRUE(pkcs1_key.get()->validate());
+    EXPECT_TRUE(pkcs1_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key.get()->compare(pkcs1_key.get(), PRIVATE));
+#endif
+
+    // PKCS#8 without encryption
+    const std::string nefile = TEST_DATA_SRCDIR "/pkcs8ne.pem";
+    boost::shared_ptr<Asym> ne_key(crypto.createAsym(nefile, "",
+                                                     RSA_, SHA1,
+                                                     PRIVATE, ASN1),
+                                   deleteAsym);
+    EXPECT_TRUE(ne_key.get()->validate());
+    EXPECT_TRUE(ne_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key.get()->compare(ne_key.get(), PRIVATE));
+}
+
+TEST(AsymTest, RSA_PRIV_DNS) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    const std::string privdnsfile =
+        TEST_DATA_SRCDIR "/Kexample.+005+18330.private";
+    boost::shared_ptr<Asym> dns_key(crypto.createAsym(privdnsfile, "",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, DNS),
+                                    deleteAsym);
+    EXPECT_TRUE(dns_key.get()->validate());
+    EXPECT_EQ(1024, dns_key.get()->getKeySize());
+
+#ifndef WITH_BOTAN
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA1, PRIVATE, BASIC),
+                 BadKey);
+#endif
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA1, PRIVATE, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, MD5, PRIVATE, DNS),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA256, PRIVATE, DNS),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA512, PRIVATE, DNS),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA224, PRIVATE, DNS),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(privdnsfile, "",
+                                   RSA_, SHA384, PRIVATE, DNS),
+                 UnsupportedAlgorithm);
+
+    boost::shared_ptr<Asym> ref_key(crypto.createAsym(privfile, "1234",
+                                                      RSA_, SHA1,
+                                                      PRIVATE, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(dns_key.get()->compare(ref_key.get(), PRIVATE));
+    EXPECT_TRUE(ref_key.get()->compare(dns_key.get(), PRIVATE));
+    EXPECT_TRUE(dns_key.get()->compare(ref_key.get(), PUBLIC));
+    EXPECT_FALSE(dns_key.get()->compare(ref_key.get(), CERT));
+
+    char tempname[] = "/tmp/privateXXXXXX";
+    const std::string testfile = mktemp(tempname);
+    ref_key.get()->exportkey(testfile, "", PRIVATE, DNS);
+    FILE* fp;
+    fp = fopen(testfile.c_str(), "r");
+    ASSERT_TRUE(fp != NULL);
+    std::vector<char> testbuf(1024);
+    size_t tcc = fread(&testbuf[0], 1024, 1, fp);
+    testbuf.resize(tcc);
+    fclose(fp);
+    fp = fopen(privdnsfile.c_str(), "r");
+    ASSERT_TRUE(fp != NULL);
+    std::vector<char> refbuf(1024);
+    int rcc = fread(&refbuf[0], 1024, 1, fp);
+    refbuf.resize(rcc);
+    fclose(fp);
+    EXPECT_EQ(rcc, tcc);
+    EXPECT_TRUE(testbuf == refbuf);
+    unlink(testfile.c_str());
+}
+
+TEST(AsymTest, CERTIFICATE) {
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> from_file(crypto.createAsym(certfile, "",
+                                                        RSA_, SHA1,
+                                                        CERT, ASN1),
+                                      deleteAsym);
+    EXPECT_TRUE(from_file.get()->validate());
+    EXPECT_EQ(1024, from_file.get()->getKeySize());
+
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, PUBLIC, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, PRIVATE, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, CERT, BASIC),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA1, CERT, DNS),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, MD5, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA224, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA256, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA384, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(certfile, "", RSA_, SHA512, CERT, ASN1),
+                 BadKey);
+
+    boost::shared_ptr<Asym> pub_key(crypto.createAsym(pubfile, "",
+                                                      RSA_, SHA1,
+                                                      PUBLIC, ASN1),
+                                    deleteAsym);
+    EXPECT_TRUE(from_file.get()->compare(pub_key.get(), PUBLIC));
+    EXPECT_TRUE(pub_key.get()->compare(from_file.get(), PUBLIC));
+    EXPECT_FALSE(from_file.get()->compare(pub_key.get(), PRIVATE));
+    EXPECT_FALSE(pub_key.get()->compare(from_file.get(), PRIVATE));
+
+    std::vector<uint8_t> certbin = from_file.get()->exportkey(CERT, ASN1);
+    boost::shared_ptr<Asym> from_bin(crypto.createAsym(&certbin[0],
+                                                       certbin.size(),
+                                                       RSA_, SHA1,
+                                                       CERT, ASN1),
+                                     deleteAsym);
+    EXPECT_TRUE(from_bin.get()->validate());
+    EXPECT_TRUE(from_file.get()->compare(from_bin.get(), PUBLIC));
+    EXPECT_TRUE(from_bin.get()->compare(from_file.get(), PUBLIC));
+    EXPECT_TRUE(from_file.get()->compare(from_bin.get(), CERT));
+    EXPECT_TRUE(from_bin.get()->compare(from_file.get(), CERT));
+
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size() - 1,
+                                   RSA_, SHA1, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA1, CERT, BASIC),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA1, CERT, DNS),
+                 UnsupportedAlgorithm);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, MD5, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA224, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA256, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA384, CERT, ASN1),
+                 BadKey);
+    EXPECT_THROW(crypto.createAsym(&certbin[0], certbin.size(),
+                                   RSA_, SHA512, CERT, ASN1),
+                 BadKey);
+
+    certbin[certbin.size() - 1]  = ~certbin[certbin.size() - 1];
+    boost::shared_ptr<Asym> bad_bin(crypto.createAsym(&certbin[0],
+                                                      certbin.size(),
+                                                      RSA_, SHA1,
+                                                      CERT, ASN1),
+                                    deleteAsym);
+    EXPECT_FALSE(bad_bin.get()->validate());
+}
index fe597f03d2dae493f284989ea3c71d38297dcb03..34320a96bce07a380d724634464f9a3ae1723163 100644 (file)
@@ -293,7 +293,6 @@ TEST(HMACTest, HMAC_MD5_RFC2202_SIGN) {
                std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected7, 16);
 }
 
-// Temporarily disabled
 TEST(HMACTest, HMAC_MD5_RFC2202_SIGN_TRUNCATED) {
     const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
                                 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
@@ -378,7 +377,6 @@ TEST(HMACTest, HMAC_SHA1_RFC2202_SIGN) {
                std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected7, 20);
 }
 
-// Temporarily disabled
 TEST(HMACTest, HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
     const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
                                 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
diff --git a/src/lib/cryptolink/tests/testdata/Kexample.+005+18330.key b/src/lib/cryptolink/tests/testdata/Kexample.+005+18330.key
new file mode 100644 (file)
index 0000000..9a9dd2d
--- /dev/null
@@ -0,0 +1 @@
+example.       3600    IN      DNSKEY  256 3 5 AwEAAbMlwgHujJFdo+pVkKIss7E1GHuhAIBPITKU0aLDGrwK7s1/IxJd+JLLh2n9hQ8lV4hpS16TEjqTqSNkD3yqIMpWk4HpZLIgrVUlh7GFyNb+X6nNUy6vnlOk+7rvMFOdAe63/6reevpX+99j85x3LqSXEDDBFUhrAR5XjV3r/ELh
diff --git a/src/lib/cryptolink/tests/testdata/Kexample.+005+18330.private b/src/lib/cryptolink/tests/testdata/Kexample.+005+18330.private
new file mode 100644 (file)
index 0000000..53f6114
--- /dev/null
@@ -0,0 +1,10 @@
+Private-key-format: v1.2
+Algorithm: 5 (RSASHA1)
+Modulus: syXCAe6MkV2j6lWQoiyzsTUYe6EAgE8hMpTRosMavAruzX8jEl34ksuHaf2FDyVXiGlLXpMSOpOpI2QPfKogylaTgelksiCtVSWHsYXI1v5fqc1TLq+eU6T7uu8wU50B7rf/qt56+lf732PznHcupJcQMMEVSGsBHleNXev8QuE=
+PublicExponent: AQAB
+PrivateExponent: qMmTX+SU9kUmshuKGPJLH1QqTBjmcv2bBqAmX9a5MqCOXHlD3wNAt3YhkKA3JI0HStQCGgoxa5VCD8EvwkI8fzOzVC+D+F1/hqXovq5AQSBdomaagmYD/PBiJAglmcYr7515VCGRYnblCzDnlpz6RfFvp2uUonA93XRtTI6O5wE=
+Prime1: 2jbe0woVIKx5p+CmVWnqzffn89r269zbfN0TbKr9bph7VCXAO335tVuXL+bBTX1eO1GcxrPUI0O/QphQA/sLUQ==
+Prime2: 0iscKYMB/UFJ+5WJfK+Bgc7Rd19lYWEH1UVYxd1za2M6gpKtpnGhz+O1EFJC5XaKG8pqUp1UeAfPHu3k8vK6kQ==
+Exponent1: bvakDJD9+WV7X6DfNGPt4NsFen2IPpxKiI4rCIFS6mBjpoCj5hzDVDPFB7jB51OvDV8M6AYeA+i5Y3XsinmoYQ==
+Exponent2: fG0pbyows09EDb6qdzcw5Tkcqh/AVbasfIdh10MUYi+NJGDSjggYVIvrVo5dLJzRh0J/UAn1SFrXNOiC85LhAQ==
+Coefficient: E1+XgBBmVAVVBQM3luDApyV7cErSDs6CQj65Wi2HyvqqguTt27+/JptQhJ6w0BC0eVYc0UmhT/hSbrQbGl9IDA==
diff --git a/src/lib/cryptolink/tests/testdata/pkcs8.pem b/src/lib/cryptolink/tests/testdata/pkcs8.pem
new file mode 100644 (file)
index 0000000..a6d6b68
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI4FAzaJbMbUsCAggA
+MBQGCCqGSIb3DQMHBAgFw/I6ZeojdgSCAoA5w0RKNMrFcc2g5ydiX3Vf/imMb9Bd
+nN/AwHTdI/YMvwDi7jRvrJQJJX4oGDmoY9fAbvenYKBoqDt8wJUpY71cSHUtKC/T
+zx/3kQJx67/pQJMtcBI4BXGtJWW7yrGW4cTZnQpwOZWBdytzAkAO1FHWxH/YoMad
+0TJ86WPUba5zQAsPojUMDeh/aPdrNUHL/Jdih7JR+tI1Drm5pvr/AAIqOBmiRfSe
+K0Zz7Y3yFoawBbHwUaa+fxb6yv48QYsFWep1ahlTNIT6bM11x56lvtTnsBFKUPSN
+FH45DC0nJpNSb0qRuh9I85G1/Eq03by5Bse5olGJilhGZaOGKdtgi09i86S3Bk0J
+vxjrjZWU4b2eapXyT5AixWVe92mZYxjTad5lA6la0ZQdy8ULaON6UNqVYJL4fgQ5
+umMCPPQl7n1HyiaWCFf8cYzTr2ACF+w8HUD23zEXTTy3S0ajZbHzq//h7hMLDuE6
+fdCDn8kONHLbzw2tZFBQhpJLVjYNbaUJ1XtkpLXT8/40a+VTb5DcrJTXXR6xJ9iO
+L9ythk4pEVcrLcEeO8nLkVI03wFLr+03vQ+V4pdy7vsEfPwr9DAglx6G5rbwWQVD
+w4QBriXWjuimcXpbsOFKG8lCYdJXWkc3x5u/zRvdMOo5kn8z5Yopd7rS+XAa5H8k
+9VTorO4ZI9fT6Jx/4qjhLjAnwqzdcEmZr6dPS6HZ+UVBQWfKYKKi5C+PlFdkpM+/
+aSH9wIad6F7LYCkThqw/WRxsvXDPNL72R+HqMtlo1GqQRhTVcvWWrUhS2RSDBRRo
+adxsMBhbp1UqP35BP1l0YD/xfPegAZTip5xPFvpRVzNxCd3FHX5dM9mu
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/src/lib/cryptolink/tests/testdata/pkcs8ne.pem b/src/lib/cryptolink/tests/testdata/pkcs8ne.pem
new file mode 100644 (file)
index 0000000..fd31e55
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALMlwgHujJFdo+pV
+kKIss7E1GHuhAIBPITKU0aLDGrwK7s1/IxJd+JLLh2n9hQ8lV4hpS16TEjqTqSNk
+D3yqIMpWk4HpZLIgrVUlh7GFyNb+X6nNUy6vnlOk+7rvMFOdAe63/6reevpX+99j
+85x3LqSXEDDBFUhrAR5XjV3r/ELhAgMBAAECgYEAqMmTX+SU9kUmshuKGPJLH1Qq
+TBjmcv2bBqAmX9a5MqCOXHlD3wNAt3YhkKA3JI0HStQCGgoxa5VCD8EvwkI8fzOz
+VC+D+F1/hqXovq5AQSBdomaagmYD/PBiJAglmcYr7515VCGRYnblCzDnlpz6RfFv
+p2uUonA93XRtTI6O5wECQQDaNt7TChUgrHmn4KZVaerN9+fz2vbr3Nt83RNsqv1u
+mHtUJcA7ffm1W5cv5sFNfV47UZzGs9QjQ79CmFAD+wtRAkEA0iscKYMB/UFJ+5WJ
+fK+Bgc7Rd19lYWEH1UVYxd1za2M6gpKtpnGhz+O1EFJC5XaKG8pqUp1UeAfPHu3k
+8vK6kQJAbvakDJD9+WV7X6DfNGPt4NsFen2IPpxKiI4rCIFS6mBjpoCj5hzDVDPF
+B7jB51OvDV8M6AYeA+i5Y3XsinmoYQJAfG0pbyows09EDb6qdzcw5Tkcqh/AVbas
+fIdh10MUYi+NJGDSjggYVIvrVo5dLJzRh0J/UAn1SFrXNOiC85LhAQJAE1+XgBBm
+VAVVBQM3luDApyV7cErSDs6CQj65Wi2HyvqqguTt27+/JptQhJ6w0BC0eVYc0Umh
+T/hSbrQbGl9IDA==
+-----END PRIVATE KEY-----
diff --git a/src/lib/cryptolink/tests/testdata/priv.pem b/src/lib/cryptolink/tests/testdata/priv.pem
new file mode 100644 (file)
index 0000000..2e328f5
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCzJcIB7oyRXaPqVZCiLLOxNRh7oQCATyEylNGiwxq8Cu7NfyMS
+XfiSy4dp/YUPJVeIaUtekxI6k6kjZA98qiDKVpOB6WSyIK1VJYexhcjW/l+pzVMu
+r55TpPu67zBTnQHut/+q3nr6V/vfY/Ocdy6klxAwwRVIawEeV41d6/xC4QIDAQAB
+AoGBAKjJk1/klPZFJrIbihjySx9UKkwY5nL9mwagJl/WuTKgjlx5Q98DQLd2IZCg
+NySNB0rUAhoKMWuVQg/BL8JCPH8zs1Qvg/hdf4al6L6uQEEgXaJmmoJmA/zwYiQI
+JZnGK++deVQhkWJ25Qsw55ac+kXxb6drlKJwPd10bUyOjucBAkEA2jbe0woVIKx5
+p+CmVWnqzffn89r269zbfN0TbKr9bph7VCXAO335tVuXL+bBTX1eO1GcxrPUI0O/
+QphQA/sLUQJBANIrHCmDAf1BSfuViXyvgYHO0XdfZWFhB9VFWMXdc2tjOoKSraZx
+oc/jtRBSQuV2ihvKalKdVHgHzx7t5PLyupECQG72pAyQ/flle1+g3zRj7eDbBXp9
+iD6cSoiOKwiBUupgY6aAo+Ycw1QzxQe4wedTrw1fDOgGHgPouWN17Ip5qGECQHxt
+KW8qMLNPRA2+qnc3MOU5HKofwFW2rHyHYddDFGIvjSRg0o4IGFSL61aOXSyc0YdC
+f1AJ9Uha1zTogvOS4QECQBNfl4AQZlQFVQUDN5bgwKcle3BK0g7OgkI+uVoth8r6
+qoLk7du/vyabUISesNAQtHlWHNFJoU/4Um60GxpfSAw=
+-----END RSA PRIVATE KEY-----
diff --git a/src/lib/cryptolink/tests/testdata/pub.pem b/src/lib/cryptolink/tests/testdata/pub.pem
new file mode 100644 (file)
index 0000000..4778476
--- /dev/null
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzJcIB7oyRXaPqVZCiLLOxNRh7
+oQCATyEylNGiwxq8Cu7NfyMSXfiSy4dp/YUPJVeIaUtekxI6k6kjZA98qiDKVpOB
+6WSyIK1VJYexhcjW/l+pzVMur55TpPu67zBTnQHut/+q3nr6V/vfY/Ocdy6klxAw
+wRVIawEeV41d6/xC4QIDAQAB
+-----END PUBLIC KEY-----
diff --git a/src/lib/cryptolink/tests/testdata/x509.pem b/src/lib/cryptolink/tests/testdata/x509.pem
new file mode 100644 (file)
index 0000000..0c03dc3
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICBDCCAW2gAwIBAgICB9EwDQYJKoZIhvcNAQEFBQAwETEPMA0GA1UEAxMGZm9v
+YmFyMB4XDTE0MTEyMzIxNTA0MloXDTI0MTEyMDIxNTA0MlowETEPMA0GA1UEAxMG
+Zm9vYmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzJcIB7oyRXaPqVZCi
+LLOxNRh7oQCATyEylNGiwxq8Cu7NfyMSXfiSy4dp/YUPJVeIaUtekxI6k6kjZA98
+qiDKVpOB6WSyIK1VJYexhcjW/l+pzVMur55TpPu67zBTnQHut/+q3nr6V/vfY/Oc
+dy6klxAwwRVIawEeV41d6/xC4QIDAQABo2swaTAdBgNVHQ4EFgQU8RVM5wRmnec8
+Ge/RIubkJDEywYwwOgYDVR0jBDMwMYAU8RVM5wRmnec8Ge/RIubkJDEywYyhFaQT
+MBExDzANBgNVBAMTBmZvb2JhcoICB9EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
+AQUFAAOBgQBsdnsubKTNLETEdhxzKV4nzvJA7o4U2pDLzYcXfIMHzwveNlFAEgVw
+vJnwTnN80Uprg4IVhdAgsy6raIHQkJmzYzx6dY6+liJMqj0sORUjIdcYUavptMnm
+nklBOgEAicLDuMFQmZXk7Iw6dDVdp1lAVHnBFTpPoVM41nqxOewJAQ==
+-----END CERTIFICATE-----