From 00d29efda6bf1abd8c002a26480af82dd7bc212a Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Sat, 6 Jun 2015 16:34:47 +0200 Subject: [PATCH] [sedhcpv6] spit xxx_asym.cc to xxx_rsa.{h,cc} --- src/lib/cryptolink/Makefile.am | 2 + src/lib/cryptolink/botan_asym.cc | 1186 +---------------------- src/lib/cryptolink/botan_rsa.cc | 1139 ++++++++++++++++++++++ src/lib/cryptolink/botan_rsa.h | 146 +++ src/lib/cryptolink/openssl_asym.cc | 1416 +--------------------------- src/lib/cryptolink/openssl_rsa.cc | 1370 +++++++++++++++++++++++++++ src/lib/cryptolink/openssl_rsa.h | 148 +++ 7 files changed, 2807 insertions(+), 2600 deletions(-) create mode 100644 src/lib/cryptolink/botan_rsa.cc create mode 100644 src/lib/cryptolink/botan_rsa.h create mode 100644 src/lib/cryptolink/openssl_rsa.cc create mode 100644 src/lib/cryptolink/openssl_rsa.h diff --git a/src/lib/cryptolink/Makefile.am b/src/lib/cryptolink/Makefile.am index e7f69ab3fe..980c1157d7 100644 --- a/src/lib/cryptolink/Makefile.am +++ b/src/lib/cryptolink/Makefile.am @@ -18,6 +18,7 @@ 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 +libkea_cryptolink_la_SOURCES += botan_rsa.h botan_rsa.cc endif if HAVE_OPENSSL libkea_cryptolink_la_SOURCES += openssl_link.cc @@ -25,6 +26,7 @@ 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 +libkea_cryptolink_la_SOURCES += openssl_rsa.h openssl_rsa.cc endif libkea_cryptolink_la_LDFLAGS = ${CRYPTO_LDFLAGS} diff --git a/src/lib/cryptolink/botan_asym.cc b/src/lib/cryptolink/botan_asym.cc index fd09f667fa..26cb75c5c8 100644 --- a/src/lib/cryptolink/botan_asym.cc +++ b/src/lib/cryptolink/botan_asym.cc @@ -15,23 +15,15 @@ #include #include -#include - #include #include -#include -#include #include #include #include -#include #include #include -#include - -#include -#include +#include namespace { @@ -44,1182 +36,6 @@ int hook_point_validate_certificate = namespace isc { namespace cryptolink { -/// @brief Botan implementation of asymmetrical cryptography (Asym). -// Each method is the counterpart of the Asym corresponding method. -class RsaAsymImpl : public 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 hash_algorithm The hash algorithm - /// @param key_kind The key kind - /// @param key_format The key binary format - RsaAsymImpl(const void* key, size_t key_len, - const HashAlgorithm hash_algorithm, - const AsymKeyKind key_kind, - const AsymFormat key_format) { - algo_ = RSA_; - hash_ = hash_algorithm; - kind_ = key_kind; - std::string hash = btn::getHashAlgorithmName(hash_); - if (hash.compare("Unknown") == 0) { - isc_throw(UnsupportedAlgorithm, - "Unknown hash algorithm: " << - static_cast(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(key); - const Botan::MemoryVector 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(key_format)); - } else if ((kind_ == PUBLIC) && (key_format == BASIC)) { - // PKCS#1 Public Key - const Botan::byte* keyin = - reinterpret_cast(key); - const Botan::MemoryVector 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(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(key)); - if (!pub_) { - 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(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(key_format)); - } else if ((kind_ == CERT) && (key_format == ASN1)) { - // X.509 Public Key Certificate - const Botan::byte* keyin = - reinterpret_cast(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_->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(hash_)); - } - Botan::Public_Key* key = x509_->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(key)); - if (!pub_) { - delete key; - x509_.reset(); - isc_throw(LibraryError, "dynamic_cast"); - } - } else if (kind_ == CERT) { - isc_throw(UnsupportedAlgorithm, - "Unknown Certificate format: " << - static_cast(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(kind_)); - } - - if (kind_ == PRIVATE) { - try { - if (!pub_) { - pub_.reset(new Botan::RSA_PublicKey(priv_->get_n(), - priv_->get_e())); - } - } catch (const std::exception& exc) { - isc_throw(BadKey, "priv to pub: " << exc.what()); - } - try { - signer_.reset(new Botan::PK_Signer(*priv_, emsa)); - } catch (const std::exception& exc) { - isc_throw(BadKey, "PK_Signer: " << exc.what()); - } - } else { - try { - verifier_.reset(new Botan::PK_Verifier(*pub_, 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 hash_algorithm The hash algorithm - /// @param key_kind The key kind - /// @param key_format The key binary format - RsaAsymImpl(const std::string& filename, - const std::string& password, - const HashAlgorithm hash_algorithm, - const AsymKeyKind key_kind, - const AsymFormat key_format) { - algo_ = RSA_; - hash_ = hash_algorithm; - kind_ = key_kind; - std::string hash = btn::getHashAlgorithmName(hash_); - if (hash.compare("Unknown") == 0) { - isc_throw(UnsupportedAlgorithm, - "Unknown hash algorithm: " << - static_cast(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(key)); - if (!priv_) { - 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(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 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 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 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 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 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(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(key)); - if (!pub_) { - 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(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 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(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_->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(hash_)); - } - Botan::Public_Key* key; - try { - key = x509_->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(key)); - if (!pub_) { - delete key; - x509_.reset(); - isc_throw(LibraryError, "dynamic_cast"); - } - } else if (kind_ == CERT) { - isc_throw(UnsupportedAlgorithm, - "Unknown Public Key Certificate format: " << - static_cast(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(kind_)); - } - - if (kind_ == PRIVATE) { - try { - if (!pub_) { - pub_.reset(new Botan::RSA_PublicKey(priv_->get_n(), - priv_->get_e())); - } - } catch (const std::exception& exc) { - isc_throw(BadKey, "priv to pub: " << exc.what()); - } - try { - signer_.reset(new Botan::PK_Signer(*priv_, emsa)); - } catch (const std::exception& exc) { - isc_throw(BadKey, "PK_Signer: " << exc.what()); - } - } else { - try { - verifier_.reset(new Botan::PK_Verifier(*pub_, emsa)); - } catch (const std::exception& exc) { - isc_throw(BadKey, "PK_Verifier: " << exc.what()); - } - } - } - - /// @brief Destructor - ~RsaAsymImpl() { } - - /// @brief Returns the AsymAlgorithm of the object - AsymAlgorithm getAsymAlgorithm() const { - return (algo_); - } - - /// @brief Returns the HashAlgorithm of the object - HashAlgorithm getHashAlgorithm() const { - return (hash_); - } - - /// @brief Returns the AsymKeyKind of the object - AsymKeyKind getAsymKeyKind() const { - return (kind_); - } - - /// @brief Returns the key size in bits - /// - size_t getKeySize() const { - if (kind_ == PRIVATE) { - return (priv_->get_n().bits()); - } else { - return (pub_->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 (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_n().bytes()); - } else { - return (pub_->get_n().bytes()); - } - default: - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Signature format: " << - static_cast(sig_format)); - } - } - - /// @brief Add data to digest - /// - /// See @ref isc::cryptolink::AsymBase::update() for details. - void update(const void* data, const size_t len) { - try { - if (kind_ == PRIVATE) { - signer_->update( - reinterpret_cast(data), len); - } else { - verifier_->update( - reinterpret_cast(data), len); - } - } catch (const std::exception& exc) { - isc_throw(LibraryError, "update: " << exc.what()); - } - } - - /// @brief Calculate the final signature - /// - /// See @ref isc::cryptolink::AsymBase::sign() for details. - void sign(isc::util::OutputBuffer& result, size_t len, - const AsymFormat) { - try { - Botan::SecureVector b_result; - Botan::AutoSeeded_RNG rng; - b_result = signer_->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::AsymBase::sign() for details. - void sign(void* result, size_t len, const AsymFormat sig_format) { - try { - Botan::SecureVector b_result; - Botan::AutoSeeded_RNG rng; - b_result = signer_->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::AsymBase::sign() for details. - std::vector sign(size_t len, const AsymFormat) { - try { - Botan::SecureVector b_result; - Botan::AutoSeeded_RNG rng; - b_result = signer_->signature(rng); - if (len > b_result.size()) { - return (std::vector(b_result.begin(), b_result.end())); - } else { - return (std::vector(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::AsymBase::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(sig); - try { - return verifier_->check_signature(sigbuf, len); - } catch (const std::exception& exc) { - isc_throw(LibraryError, "check_signature: " << exc.what()); - } - } - - /// \brief Clear the crypto state and go back to the initial state - /// (must be called before reusing an Asym object) - void clear() { - std::string hash = btn::getHashAlgorithmName(hash_); - if (hash.compare("Unknown") == 0) { - isc_throw(UnsupportedAlgorithm, - "Unknown hash algorithm: " << - static_cast(hash_)); - } - std::string emsa = "EMSA3(" + hash + ")"; - if (kind_ == PRIVATE) { - try { - signer_.reset(new Botan::PK_Signer(*priv_, emsa)); - } catch (const std::exception& exc) { - isc_throw(BadKey, "PK_Signer: " << exc.what()); - } - } else { - try { - verifier_.reset(new Botan::PK_Verifier(*pub_, emsa)); - } catch (const std::exception& exc) { - isc_throw(BadKey, "PK_Verifier: " << exc.what()); - } - } - } - - /// @brief Export the key value (binary) - /// - /// See @ref isc::cryptolink::AsymBase::exportkey() for details - std::vector 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 der; - try { - der = priv_->pkcs8_private_key(); - } catch (const std::exception& exc) { - isc_throw(LibraryError, "pkcs8_private_key: " << exc.what()); - } - return std::vector(der.begin(), der.end()); - } else if (key_kind == PRIVATE) { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Private Key format: " << - static_cast(key_format)); - } else if ((key_kind == PUBLIC) && (key_format == BASIC)) { - // PKCS#1 Public Key - Botan::MemoryVector der; - try { - der = pub_->x509_subject_public_key(); - } catch (const std::exception& exc) { - isc_throw(LibraryError, - "x509_subject_public_key: " - << exc.what()); - } - return std::vector(der.begin(), der.end()); - } else if ((key_kind == PUBLIC) && (key_format == ASN1)) { - // SubjectPublicKeyInfo - Botan::MemoryVector ber; - try { - ber = Botan::X509::BER_encode(*pub_); - } catch (const std::exception& exc) { - isc_throw(LibraryError, "X509::BER_encode: " << exc.what()); - } - return std::vector(ber.begin(), ber.end()); - } else if ((key_kind == PUBLIC) && (key_format == DNS)) { - // RFC 3110 DNS wire format - size_t e_bytes = pub_->get_e().bytes(); - size_t mod_bytes = pub_->get_n().bytes(); - size_t x_bytes = 1; - if (e_bytes >= 256) { - x_bytes += 2; - } - std::vector 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_e().binary_encode(&rdata[x_bytes]); - pub_->get_n().binary_encode(&rdata[x_bytes + e_bytes]); - return rdata; - } else if (key_kind == PUBLIC) { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Public Key format: " << - static_cast(key_format)); - } else if ((key_kind == CERT) && (key_format == ASN1)) { - // X.509 Public Key Certificate - if (kind_ != CERT) { - isc_throw(UnsupportedAlgorithm, "Have no Certificate"); - } - Botan::MemoryVector ber; - try { - ber = x509_->BER_encode(); - } catch (const std::exception& exc) { - isc_throw(LibraryError, "BER_encode" << exc.what()); - } - return std::vector(ber.begin(), ber.end()); - } else if (key_kind == CERT) { - isc_throw(UnsupportedAlgorithm, - "Unknown Certificate format: " << - static_cast(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(key_kind)); - } - } - - /// @brief Export the key value (file) - /// - /// See @ref isc::cryptolink::AsymBase::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_, 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(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 bin; - bin.resize(priv_->get_n().bytes()); - priv_->get_n().binary_encode(&bin[0]); - fp << "Modulus: " << util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_e().bytes()); - priv_->get_e().binary_encode(&bin[0]); - fp << "PublicExponent: " << - util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_d().bytes()); - priv_->get_d().binary_encode(&bin[0]); - fp << "PrivateExponent: " << - util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_p().bytes()); - priv_->get_p().binary_encode(&bin[0]); - fp << "Prime1: " << util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_q().bytes()); - priv_->get_q().binary_encode(&bin[0]); - fp << "Prime2: " << util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_d1().bytes()); - priv_->get_d1().binary_encode(&bin[0]); - fp << "Exponent1: " << util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_d2().bytes()); - priv_->get_d2().binary_encode(&bin[0]); - fp << "Exponent2: " << util::encode::encodeBase64(bin) << '\n'; - bin.resize(priv_->get_c().bytes()); - priv_->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(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_); - } 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 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(key_format)); - } else if ((key_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_->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 (key_kind == CERT) { - if (!x509_) { - isc_throw(UnsupportedAlgorithm, "Have no Certificate"); - } - isc_throw(UnsupportedAlgorithm, - "Unknown Certificate format: " << - static_cast(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(key_kind)); - } - } - - /// @brief Check the validity - /// - /// See @ref isc::cryptolink::AsymBase::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_->check_key(rng, true); - } catch (const std::exception& exc) { - isc_throw(LibraryError, "check_key: " << exc.what()); - } - case PRIVATE: - try { - return priv_->check_key(rng, true); - } catch (const std::exception& exc) { - isc_throw(LibraryError, "check_key: " << exc.what()); - } - case CERT: - store.add_cert(*x509_, true); - status = store.validate_cert(*x509_); - 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 RsaAsymImpl* other, const AsymKeyKind key_kind) const { - if (!other || (other->algo_ != RSA_)) { - return false; - } - Botan::BigInt e, n; - switch (key_kind) { - case CERT: - // Special case for cert - cert - if ((kind_ == CERT) && (other->kind_ == CERT)) { - return (*x509_ == *other->x509_); - } - // 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_e(); - n = priv_->get_n();; - } else if ((kind_ == PUBLIC) || (kind_ == CERT)) { - e = pub_->get_e(); - n = pub_->get_n(); - } else { - return false; - } - if (other->kind_ == PRIVATE) { - return ((e == other->priv_->get_e()) && - (n == other->priv_->get_n())); - } else if ((other->kind_ == PUBLIC) || (other->kind_ == CERT)) { - return ((e == other->pub_->get_e()) && - (n == other->pub_->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_e() == other->priv_->get_e()) && - (priv_->get_n() == other->priv_->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 priv_; - /// @brief The protected pointer to the Botan public key - boost::scoped_ptr pub_; - /// @brief The protected pointer to the Botan certificate - boost::scoped_ptr x509_; - /// @brief The protected pointer to the Botan signer - boost::scoped_ptr signer_; - /// @brief The protected pointer to the Botan verifier - boost::scoped_ptr verifier_; -}; - Asym::Asym(const void* key, size_t key_len, const AsymAlgorithm asym_algorithm, const HashAlgorithm hash_algorithm, diff --git a/src/lib/cryptolink/botan_rsa.cc b/src/lib/cryptolink/botan_rsa.cc new file mode 100644 index 0000000000..e8ac591bb7 --- /dev/null +++ b/src/lib/cryptolink/botan_rsa.cc @@ -0,0 +1,1139 @@ +// 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 +// 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace isc { +namespace cryptolink { + +/// @brief Constructor from a key, asym and hash algorithm, +/// key kind and key binary format +RsaAsymImpl::RsaAsymImpl(const void* key, size_t key_len, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format) { + algo_ = RSA_; + hash_ = hash_algorithm; + kind_ = key_kind; + std::string hash = btn::getHashAlgorithmName(hash_); + if (hash.compare("Unknown") == 0) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << static_cast(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(key); + const Botan::MemoryVector 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(key_format)); + } else if ((kind_ == PUBLIC) && (key_format == BASIC)) { + // PKCS#1 Public Key + const Botan::byte* keyin = reinterpret_cast(key); + const Botan::MemoryVector 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(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(key)); + if (!pub_) { + 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(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(key_format)); + } else if ((kind_ == CERT) && (key_format == ASN1)) { + // X.509 Public Key Certificate + const Botan::byte* keyin = reinterpret_cast(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_->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(hash_)); + } + Botan::Public_Key* key = x509_->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(key)); + if (!pub_) { + delete key; + x509_.reset(); + isc_throw(LibraryError, "dynamic_cast"); + } + } else if (kind_ == CERT) { + isc_throw(UnsupportedAlgorithm, + "Unknown Certificate format: " << + static_cast(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << static_cast(kind_)); + } + + if (kind_ == PRIVATE) { + try { + if (!pub_) { + pub_.reset(new Botan::RSA_PublicKey(priv_->get_n(), + priv_->get_e())); + } + } catch (const std::exception& exc) { + isc_throw(BadKey, "priv to pub: " << exc.what()); + } + try { + signer_.reset(new Botan::PK_Signer(*priv_, emsa)); + } catch (const std::exception& exc) { + isc_throw(BadKey, "PK_Signer: " << exc.what()); + } + } else { + try { + verifier_.reset(new Botan::PK_Verifier(*pub_, 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 +RsaAsymImpl::RsaAsymImpl(const std::string& filename, + const std::string& password, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format) { + algo_ = RSA_; + hash_ = hash_algorithm; + kind_ = key_kind; + std::string hash = btn::getHashAlgorithmName(hash_); + if (hash.compare("Unknown") == 0) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << static_cast(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(key)); + if (!priv_) { + 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(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 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 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 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 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 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(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(key)); + if (!pub_) { + 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(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 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(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_->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(hash_)); + } + Botan::Public_Key* key; + try { + key = x509_->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(key)); + if (!pub_) { + delete key; + x509_.reset(); + isc_throw(LibraryError, "dynamic_cast"); + } + } else if (kind_ == CERT) { + isc_throw(UnsupportedAlgorithm, + "Unknown Public Key Certificate format: " << + static_cast(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << static_cast(kind_)); + } + + if (kind_ == PRIVATE) { + try { + if (!pub_) { + pub_.reset(new Botan::RSA_PublicKey(priv_->get_n(), + priv_->get_e())); + } + } catch (const std::exception& exc) { + isc_throw(BadKey, "priv to pub: " << exc.what()); + } + try { + signer_.reset(new Botan::PK_Signer(*priv_, emsa)); + } catch (const std::exception& exc) { + isc_throw(BadKey, "PK_Signer: " << exc.what()); + } + } else { + try { + verifier_.reset(new Botan::PK_Verifier(*pub_, emsa)); + } catch (const std::exception& exc) { + isc_throw(BadKey, "PK_Verifier: " << exc.what()); + } + } +} + +/// @brief Destructor +RsaAsymImpl::~RsaAsymImpl() { } + +/// @brief Returns the AsymAlgorithm of the object +AsymAlgorithm RsaAsymImpl::getAsymAlgorithm() const { + return (algo_); +} + +/// @brief Returns the HashAlgorithm of the object +HashAlgorithm RsaAsymImpl::getHashAlgorithm() const { + return (hash_); +} + +/// @brief Returns the AsymKeyKind of the object +AsymKeyKind RsaAsymImpl::getAsymKeyKind() const { + return (kind_); +} + +/// @brief Returns the key size in bits +size_t RsaAsymImpl::getKeySize() const { + if (kind_ == PRIVATE) { + return (priv_->get_n().bits()); + } else { + return (pub_->get_n().bits()); + } +} + +/// @brief Returns the output size of the signature +size_t RsaAsymImpl::getSignatureLength(const AsymFormat sig_format) const { + 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_n().bytes()); + } else { + return (pub_->get_n().bytes()); + } + default: + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Signature format: " << + static_cast(sig_format)); + } +} + +/// @brief Add data to digest +void RsaAsymImpl::update(const void* data, const size_t len) { + try { + if (kind_ == PRIVATE) { + signer_->update(reinterpret_cast(data), len); + } else { + verifier_->update(reinterpret_cast(data), len); + } + } catch (const std::exception& exc) { + isc_throw(LibraryError, "update: " << exc.what()); + } +} + +/// @brief Calculate the final signature +void RsaAsymImpl::sign(isc::util::OutputBuffer& result, size_t len, + const AsymFormat) { + try { + Botan::SecureVector b_result; + Botan::AutoSeeded_RNG rng; + b_result = signer_->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 +void RsaAsymImpl::sign(void* result, size_t len, const AsymFormat sig_format) { + try { + Botan::SecureVector b_result; + Botan::AutoSeeded_RNG rng; + b_result = signer_->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 +std::vector RsaAsymImpl::sign(size_t len, const AsymFormat) { + try { + Botan::SecureVector b_result; + Botan::AutoSeeded_RNG rng; + b_result = signer_->signature(rng); + if (len > b_result.size()) { + return (std::vector(b_result.begin(), b_result.end())); + } else { + return (std::vector(b_result.begin(), &b_result[len])); + } + } catch (const std::exception& exc) { + isc_throw(LibraryError, "signature: " << exc.what()); + } +} + +/// @brief Verify an existing signature +bool RsaAsymImpl::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(sig); + try { + return verifier_->check_signature(sigbuf, len); + } catch (const std::exception& exc) { + isc_throw(LibraryError, "check_signature: " << exc.what()); + } +} + +/// \brief Clear the crypto state and go back to the initial state +void RsaAsymImpl::clear() { + std::string hash = btn::getHashAlgorithmName(hash_); + if (hash.compare("Unknown") == 0) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << static_cast(hash_)); + } + std::string emsa = "EMSA3(" + hash + ")"; + if (kind_ == PRIVATE) { + try { + signer_.reset(new Botan::PK_Signer(*priv_, emsa)); + } catch (const std::exception& exc) { + isc_throw(BadKey, "PK_Signer: " << exc.what()); + } + } else { + try { + verifier_.reset(new Botan::PK_Verifier(*pub_, emsa)); + } catch (const std::exception& exc) { + isc_throw(BadKey, "PK_Verifier: " << exc.what()); + } + } +} + +/// @brief Export the key value (binary) +std::vector + RsaAsymImpl::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 der; + try { + der = priv_->pkcs8_private_key(); + } catch (const std::exception& exc) { + isc_throw(LibraryError, "pkcs8_private_key: " << exc.what()); + } + return std::vector(der.begin(), der.end()); + } else if (key_kind == PRIVATE) { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Private Key format: " << + static_cast(key_format)); + } else if ((key_kind == PUBLIC) && (key_format == BASIC)) { + // PKCS#1 Public Key + Botan::MemoryVector der; + try { + der = pub_->x509_subject_public_key(); + } catch (const std::exception& exc) { + isc_throw(LibraryError, + "x509_subject_public_key: " << exc.what()); + } + return std::vector(der.begin(), der.end()); + } else if ((key_kind == PUBLIC) && (key_format == ASN1)) { + // SubjectPublicKeyInfo + Botan::MemoryVector ber; + try { + ber = Botan::X509::BER_encode(*pub_); + } catch (const std::exception& exc) { + isc_throw(LibraryError, "X509::BER_encode: " << exc.what()); + } + return std::vector(ber.begin(), ber.end()); + } else if ((key_kind == PUBLIC) && (key_format == DNS)) { + // RFC 3110 DNS wire format + size_t e_bytes = pub_->get_e().bytes(); + size_t mod_bytes = pub_->get_n().bytes(); + size_t x_bytes = 1; + if (e_bytes >= 256) { + x_bytes += 2; + } + std::vector 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_e().binary_encode(&rdata[x_bytes]); + pub_->get_n().binary_encode(&rdata[x_bytes + e_bytes]); + return rdata; + } else if (key_kind == PUBLIC) { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Public Key format: " << + static_cast(key_format)); + } else if ((key_kind == CERT) && (key_format == ASN1)) { + // X.509 Public Key Certificate + if (kind_ != CERT) { + isc_throw(UnsupportedAlgorithm, "Have no Certificate"); + } + Botan::MemoryVector ber; + try { + ber = x509_->BER_encode(); + } catch (const std::exception& exc) { + isc_throw(LibraryError, "BER_encode" << exc.what()); + } + return std::vector(ber.begin(), ber.end()); + } else if (key_kind == CERT) { + isc_throw(UnsupportedAlgorithm, + "Unknown Certificate format: " << + static_cast(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << static_cast(key_kind)); + } +} + +/// @brief Export the key value (file) +void RsaAsymImpl::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_, 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(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 bin; + bin.resize(priv_->get_n().bytes()); + priv_->get_n().binary_encode(&bin[0]); + fp << "Modulus: " << util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_e().bytes()); + priv_->get_e().binary_encode(&bin[0]); + fp << "PublicExponent: " << + util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_d().bytes()); + priv_->get_d().binary_encode(&bin[0]); + fp << "PrivateExponent: " << + util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_p().bytes()); + priv_->get_p().binary_encode(&bin[0]); + fp << "Prime1: " << util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_q().bytes()); + priv_->get_q().binary_encode(&bin[0]); + fp << "Prime2: " << util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_d1().bytes()); + priv_->get_d1().binary_encode(&bin[0]); + fp << "Exponent1: " << util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_d2().bytes()); + priv_->get_d2().binary_encode(&bin[0]); + fp << "Exponent2: " << util::encode::encodeBase64(bin) << '\n'; + bin.resize(priv_->get_c().bytes()); + priv_->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(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_); + } 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 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(key_format)); + } else if ((key_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_->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 (key_kind == CERT) { + if (!x509_) { + isc_throw(UnsupportedAlgorithm, "Have no Certificate"); + } + isc_throw(UnsupportedAlgorithm, + "Unknown Certificate format: " << + static_cast(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << static_cast(key_kind)); + } +} + +/// @brief Check the validity +bool RsaAsymImpl::validate() const { + Botan::AutoSeeded_RNG rng; + Botan::X509_Store store; + Botan::X509_Code status; + switch (kind_) { + case PUBLIC: + // what to do? + try { + return pub_->check_key(rng, true); + } catch (const std::exception& exc) { + isc_throw(LibraryError, "check_key: " << exc.what()); + } + case PRIVATE: + try { + return priv_->check_key(rng, true); + } catch (const std::exception& exc) { + isc_throw(LibraryError, "check_key: " << exc.what()); + } + case CERT: + store.add_cert(*x509_, true); + status = store.validate_cert(*x509_); + if (status == Botan::VERIFIED) { + return true; + } + return false; + default: + return false; + } +} + +/// @brief Compare two keys +bool RsaAsymImpl::compare(const RsaAsymImpl* other, + const AsymKeyKind key_kind) const { + if (!other || (other->algo_ != RSA_)) { + return false; + } + Botan::BigInt e, n; + switch (key_kind) { + case CERT: + // Special case for cert - cert + if ((kind_ == CERT) && (other->kind_ == CERT)) { + return (*x509_ == *other->x509_); + } + // 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_e(); + n = priv_->get_n();; + } else if ((kind_ == PUBLIC) || (kind_ == CERT)) { + e = pub_->get_e(); + n = pub_->get_n(); + } else { + return false; + } + if (other->kind_ == PRIVATE) { + return ((e == other->priv_->get_e()) && + (n == other->priv_->get_n())); + } else if ((other->kind_ == PUBLIC) || (other->kind_ == CERT)) { + return ((e == other->pub_->get_e()) && + (n == other->pub_->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_e() == other->priv_->get_e()) && + (priv_->get_n() == other->priv_->get_n())); + default: + return false; + } +} + +} // namespace cryptolink +} // namespace isc diff --git a/src/lib/cryptolink/botan_rsa.h b/src/lib/cryptolink/botan_rsa.h new file mode 100644 index 0000000000..6329946fe0 --- /dev/null +++ b/src/lib/cryptolink/botan_rsa.h @@ -0,0 +1,146 @@ +// 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 +// 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. + +namespace isc { +namespace cryptolink { + +/// @brief Botan implementation of asymmetrical cryptography (Asym). +// Each method is the counterpart of the Asym corresponding method. +class RsaAsymImpl : public 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 hash_algorithm The hash algorithm + /// @param key_kind The key kind + /// @param key_format The key binary format + RsaAsymImpl(const void* key, size_t key_len, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format); + + /// @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 hash_algorithm The hash algorithm + /// @param key_kind The key kind + /// @param key_format The key binary format + RsaAsymImpl(const std::string& filename, + const std::string& password, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format); + + /// @brief Destructor + ~RsaAsymImpl(); + + /// @brief Returns the AsymAlgorithm of the object + AsymAlgorithm getAsymAlgorithm() const; + + /// @brief Returns the HashAlgorithm of the object + HashAlgorithm getHashAlgorithm() const; + + /// @brief Returns the AsymKeyKind of the object + AsymKeyKind getAsymKeyKind() const; + + /// @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 + /// + /// See @ref isc::cryptolink::AsymBase::update() for details. + void update(const void* data, const size_t len); + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::AsymBase::sign() for details. + void sign(isc::util::OutputBuffer& result, size_t len, const AsymFormat); + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::AsymBase::sign() for details. + void sign(void* result, size_t len, const AsymFormat sig_format); + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::AsymBase::sign() for details. + std::vector sign(size_t len, const AsymFormat); + + /// @brief Verify an existing signature + /// + /// See @ref isc::cryptolink::AsymBase::verify() for details. + bool verify(const void* sig, size_t len, const AsymFormat sig_format); + + /// \brief Clear the crypto state and go back to the initial state + /// (must be called before reusing an Asym object) + void clear(); + + /// @brief Export the key value (binary) + /// + /// See @ref isc::cryptolink::AsymBase::exportkey() for details + std::vector exportkey(const AsymKeyKind key_kind, + const AsymFormat key_format) const; + + /// @brief Export the key value (file) + /// + /// See @ref isc::cryptolink::AsymBase::exportkey() for details + void exportkey(const std::string& filename, + const std::string& password, + const AsymKeyKind key_kind, + const AsymFormat key_format) const; + + /// @brief Check the validity + /// + /// See @ref isc::cryptolink::AsymBase::validate() for details + bool validate() const; + + /// @brief Compare two keys + /// + /// See @ref isc::cryptolink::Asym::compare() for details + bool compare(const RsaAsymImpl* other, const AsymKeyKind key_kind) const; + +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 priv_; + /// @brief The protected pointer to the Botan public key + boost::scoped_ptr pub_; + /// @brief The protected pointer to the Botan certificate + boost::scoped_ptr x509_; + /// @brief The protected pointer to the Botan signer + boost::scoped_ptr signer_; + /// @brief The protected pointer to the Botan verifier + boost::scoped_ptr verifier_; +}; + +} // namespace cryptolink +} // namespace isc diff --git a/src/lib/cryptolink/openssl_asym.cc b/src/lib/cryptolink/openssl_asym.cc index d2e0ccb341..1907fd5dc8 100644 --- a/src/lib/cryptolink/openssl_asym.cc +++ b/src/lib/cryptolink/openssl_asym.cc @@ -15,20 +15,12 @@ #include #include -#include - #include -#include -#include -#include -#include #include #include #include - -#include -#include +#include namespace { @@ -41,1412 +33,6 @@ int hook_point_validate_certificate = namespace isc { namespace cryptolink { -/// @brief OpenSSL implementation of asymmetrical cryptography (Asym). -// Each method is the counterpart of the Asym corresponding method. -class RsaAsymImpl : public 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 hash_algorithm The hash algorithm - /// @param key_kind The key kind - /// @param key_format The key binary format - RsaAsymImpl(const void* key, size_t key_len, - const HashAlgorithm hash_algorithm, - const AsymKeyKind key_kind, - const AsymFormat key_format) { - algo_ = RSA_; - hash_ = hash_algorithm; - kind_ = key_kind; - pkey_ = NULL; - x509_ = NULL; - const EVP_MD* md = ossl::getHashAlgorithm(hash_); - if (md == 0) { - isc_throw(UnsupportedAlgorithm, - "Unknown hash algorithm: " << - static_cast(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(key); - RSA* rsa = d2i_RSAPrivateKey(NULL, &p, - static_cast(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(key_format)); - } else if ((kind_ == PUBLIC) && (key_format == BASIC)) { - // PKCS#1 Public Key - const unsigned char* p = - reinterpret_cast(key); - RSA* rsa = d2i_RSAPublicKey(NULL, &p, - static_cast(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(key); - RSA* rsa = d2i_RSA_PUBKEY(NULL, &p, static_cast(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(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(key_format)); - } else if ((kind_ == CERT) && (key_format == ASN1)) { - // X.509 Public Key Certificate - const unsigned char* p = - reinterpret_cast(key); - x509_ = d2i_X509(NULL, &p, static_cast(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(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(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(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 hash_algorithm The hash algorithm - /// @param key_kind The key kind - /// @param key_format The key binary format - RsaAsymImpl(const std::string& filename, - const std::string& password, - const HashAlgorithm hash_algorithm, - const AsymKeyKind key_kind, - const AsymFormat key_format) { - algo_ = RSA_; - hash_ = hash_algorithm; - kind_ = key_kind; - pkey_ = NULL; - x509_ = NULL; - const EVP_MD* md = ossl::getHashAlgorithm(hash_); - if (md == 0) { - isc_throw(UnsupportedAlgorithm, - "Unknown hash algorithm: " << - static_cast(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(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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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(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(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(line[last])) != NULL) { - --last; - } - const std::string value(line + last + 1); - std::vector 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(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(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(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(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 - virtual ~RsaAsymImpl() { - 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 AsymAlgorithm of the object - AsymAlgorithm getAsymAlgorithm() const { - return (algo_); - } - - /// @brief Returns the HashAlgorithm of the object - HashAlgorithm getHashAlgorithm() const { - return (hash_); - } - - /// @brief Returns the AsymKeyKind of the object - AsymKeyKind getAsymKeyKind() const { - return (kind_); - } - - /// @brief Returns the key size in bits - /// - size_t getKeySize() const { - return (static_cast(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 (sig_format) { - case BASIC: - case ASN1: - case DNS: - // In all cases a big integer of the size of n - return (static_cast(EVP_PKEY_size(pkey_))); - default: - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Signature format: " << - static_cast(sig_format)); - } - } - - /// @brief Add data to digest - /// - /// See @ref isc::cryptolink::AsymBase::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::AsymBase::sign() for details. - void sign(isc::util::OutputBuffer& result, size_t len, - const AsymFormat sig_format) { - unsigned int size = getSignatureLength(sig_format); - ossl::SecBuf 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::AsymBase::sign() for details. - void sign(void* result, size_t len, const AsymFormat sig_format) { - unsigned int size = getSignatureLength(sig_format); - ossl::SecBuf 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::AsymBase::sign() for details. - std::vector sign(size_t len, const AsymFormat sig_format) { - unsigned int size = getSignatureLength(sig_format); - ossl::SecBuf 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(sig.begin(), sig.end())); - } - - /// @brief Verify an existing signature - /// - /// See @ref isc::cryptolink::AsymBase::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(const_cast(sig)); - unsigned int siglen = static_cast(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 Clear the crypto state and go back to the initial state - /// (must be called before reusing an Asym object) - void clear() { - if (mdctx_) { - EVP_MD_CTX_cleanup(mdctx_.get()); - } else { - mdctx_.reset(new EVP_MD_CTX); - } - EVP_MD_CTX_init(mdctx_.get()); - const EVP_MD* md = ossl::getHashAlgorithm(hash_); - if (md == 0) { - isc_throw(UnsupportedAlgorithm, - "Unknown hash algorithm: " << - static_cast(hash_)); - } - 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 Export the key value (binary) - /// - /// See @ref isc::cryptolink::AsymBase::exportkey() for details - std::vector 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 der(static_cast(len)); - unsigned char* p = &der[0]; - len = i2d_RSAPrivateKey(rsa, &p); - RSA_free(rsa); - if (len != static_cast(der.size())) { - isc_throw(LibraryError, "i2d_RSAPrivateKey"); - } - return der; - } else if (key_kind == PRIVATE) { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Private Key format: " << - static_cast(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 der(static_cast(len)); - unsigned char* p = &der[0]; - len = i2d_RSAPublicKey(rsa, &p); - RSA_free(rsa); - if (len != static_cast(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 der(static_cast(len)); - unsigned char* p = &der[0]; - len = i2d_RSA_PUBKEY(rsa, &p); - RSA_free(rsa); - if (len != static_cast(der.size())) { - isc_throw(LibraryError, "i2d_RSA_PUBKEY"); - } - return der; - } else if ((key_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 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 (key_kind == PUBLIC) { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Public Key format: " << - static_cast(key_format)); - } else if ((key_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 ber(static_cast(len)); - unsigned char* p = &ber[0]; - len = i2d_X509(x509_, &p); - if (len != static_cast(ber.size())) { - isc_throw(LibraryError, "i2d_X509"); - } - return ber; - } else if (key_kind == CERT) { - isc_throw(UnsupportedAlgorithm, - "Unknown Certificate format: " << - static_cast(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(key_kind)); - } - } - - /// @brief Export the key value (file) - /// - /// See @ref isc::cryptolink::AsymBase::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(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(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(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\n"); - if (hash_ == MD5) { - fprintf(fp, "Algorithm: 1 (RSA)\n"); - } else if (hash_ == SHA1) { - fprintf(fp, "Algorithm: 5 (RSASHA1)\n"); - } else if (hash_ == SHA256) { - fprintf(fp, "Algorithm: 8 (RSASHA256)\n"); - } else if (hash_ == SHA512) { - fprintf(fp, "Algorithm: 10 (RSASHA512)\n"); - } - std::vector bin; - bin.resize(BN_num_bytes(rsa->n)); - BN_bn2bin(rsa->n, &bin[0]); - fprintf(fp, "Modulus: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->e)); - BN_bn2bin(rsa->e, &bin[0]); - fprintf(fp, "PublicExponent: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->d)); - BN_bn2bin(rsa->d, &bin[0]); - fprintf(fp, "PrivateExponent: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->p)); - BN_bn2bin(rsa->p, &bin[0]); - fprintf(fp, "Prime1: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->q)); - BN_bn2bin(rsa->q, &bin[0]); - fprintf(fp, "Prime2: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->dmp1)); - BN_bn2bin(rsa->dmp1, &bin[0]); - fprintf(fp, "Exponent1: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->dmq1)); - BN_bn2bin(rsa->dmq1, &bin[0]); - fprintf(fp, "Exponent2: %s\n", - util::encode::encodeBase64(bin).c_str()); - bin.resize(BN_num_bytes(rsa->iqmp)); - BN_bn2bin(rsa->iqmp, &bin[0]); - fprintf(fp, "Coefficient: %s\n", - 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(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(), "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"); - } - 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(), "w"); - 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 bin = exportkey(key_kind, key_format); - FILE* fp = fopen(filename.c_str(), "w"); - 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(key_format)); - } else if ((key_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(), "w"); - 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 (key_kind == CERT) { - if (!x509_) { - isc_throw(UnsupportedAlgorithm, "Have no Certificate"); - } - isc_throw(UnsupportedAlgorithm, - "Unknown Certificate format: " << - static_cast(key_format)); - } else { - isc_throw(UnsupportedAlgorithm, - "Unknown RSA Key kind: " << - static_cast(key_kind)); - } - } - - /// @brief Check the validity - /// - /// See @ref isc::cryptolink::AsymBase::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 RsaAsymImpl* other, const AsymKeyKind key_kind) const { - if (!other || (other->algo_ != RSA_)) { - return false; - } - 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 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, diff --git a/src/lib/cryptolink/openssl_rsa.cc b/src/lib/cryptolink/openssl_rsa.cc new file mode 100644 index 0000000000..8958cc3785 --- /dev/null +++ b/src/lib/cryptolink/openssl_rsa.cc @@ -0,0 +1,1370 @@ +// 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 +// 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 +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace isc { +namespace cryptolink { + +/// @brief Constructor from a key, asym and hash algorithm, +/// key kind and key binary format +RsaAsymImpl::RsaAsymImpl(const void* key, size_t key_len, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format) { + algo_ = RSA_; + hash_ = hash_algorithm; + kind_ = key_kind; + pkey_ = NULL; + x509_ = NULL; + const EVP_MD* md = ossl::getHashAlgorithm(hash_); + if (md == 0) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << static_cast(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(key); + RSA* rsa = d2i_RSAPrivateKey(NULL, &p, static_cast(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(key_format)); + } else if ((kind_ == PUBLIC) && (key_format == BASIC)) { + // PKCS#1 Public Key + const unsigned char* p = reinterpret_cast(key); + RSA* rsa = d2i_RSAPublicKey(NULL, &p, static_cast(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(key); + RSA* rsa = d2i_RSA_PUBKEY(NULL, &p, static_cast(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(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(key_format)); + } else if ((kind_ == CERT) && (key_format == ASN1)) { + // X.509 Public Key Certificate + const unsigned char* p = reinterpret_cast(key); + x509_ = d2i_X509(NULL, &p, static_cast(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(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(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << static_cast(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 +RsaAsymImpl::RsaAsymImpl(const std::string& filename, + const std::string& password, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format) { + algo_ = RSA_; + hash_ = hash_algorithm; + kind_ = key_kind; + pkey_ = NULL; + x509_ = NULL; + const EVP_MD* md = ossl::getHashAlgorithm(hash_); + if (md == 0) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << static_cast(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(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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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(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(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(line[last])) != NULL) { + --last; + } + const std::string value(line + last + 1); + std::vector 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(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(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(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << static_cast(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 +RsaAsymImpl::~RsaAsymImpl() { + 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 AsymAlgorithm of the object +AsymAlgorithm RsaAsymImpl::getAsymAlgorithm() const { + return (algo_); +} + +/// @brief Returns the HashAlgorithm of the object +HashAlgorithm RsaAsymImpl::getHashAlgorithm() const { + return (hash_); +} + +/// @brief Returns the AsymKeyKind of the object +AsymKeyKind RsaAsymImpl::getAsymKeyKind() const { + return (kind_); +} + +/// @brief Returns the key size in bits +size_t RsaAsymImpl::getKeySize() const { + return (static_cast(EVP_PKEY_bits(pkey_))); +} + +/// @brief Returns the output size of the signature +size_t RsaAsymImpl::getSignatureLength(const AsymFormat sig_format) const { + switch (sig_format) { + case BASIC: + case ASN1: + case DNS: + // In all cases a big integer of the size of n + return (static_cast(EVP_PKEY_size(pkey_))); + default: + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Signature format: " << + static_cast(sig_format)); + } +} + +/// @brief Add data to digest +void RsaAsymImpl::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 +void RsaAsymImpl::sign(isc::util::OutputBuffer& result, size_t len, + const AsymFormat sig_format) { + unsigned int size = getSignatureLength(sig_format); + ossl::SecBuf 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 +void RsaAsymImpl::sign(void* result, size_t len, const AsymFormat sig_format) { + unsigned int size = getSignatureLength(sig_format); + ossl::SecBuf 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 +std::vector RsaAsymImpl::sign(size_t len, + const AsymFormat sig_format) { + unsigned int size = getSignatureLength(sig_format); + ossl::SecBuf 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(sig.begin(), sig.end())); +} + +/// @brief Verify an existing signature +bool RsaAsymImpl::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(const_cast(sig)); + unsigned int siglen = static_cast(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 Clear the crypto state and go back to the initial state +void RsaAsymImpl::clear() { + if (mdctx_) { + EVP_MD_CTX_cleanup(mdctx_.get()); + } else { + mdctx_.reset(new EVP_MD_CTX); + } + EVP_MD_CTX_init(mdctx_.get()); + const EVP_MD* md = ossl::getHashAlgorithm(hash_); + if (md == 0) { + isc_throw(UnsupportedAlgorithm, + "Unknown hash algorithm: " << + static_cast(hash_)); + } + 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 Export the key value (binary) +std::vector +RsaAsymImpl::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 der(static_cast(len)); + unsigned char* p = &der[0]; + len = i2d_RSAPrivateKey(rsa, &p); + RSA_free(rsa); + if (len != static_cast(der.size())) { + isc_throw(LibraryError, "i2d_RSAPrivateKey"); + } + return der; + } else if (key_kind == PRIVATE) { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Private Key format: " << + static_cast(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 der(static_cast(len)); + unsigned char* p = &der[0]; + len = i2d_RSAPublicKey(rsa, &p); + RSA_free(rsa); + if (len != static_cast(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 der(static_cast(len)); + unsigned char* p = &der[0]; + len = i2d_RSA_PUBKEY(rsa, &p); + RSA_free(rsa); + if (len != static_cast(der.size())) { + isc_throw(LibraryError, "i2d_RSA_PUBKEY"); + } + return der; + } else if ((key_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 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 (key_kind == PUBLIC) { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Public Key format: " << + static_cast(key_format)); + } else if ((key_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 ber(static_cast(len)); + unsigned char* p = &ber[0]; + len = i2d_X509(x509_, &p); + if (len != static_cast(ber.size())) { + isc_throw(LibraryError, "i2d_X509"); + } + return ber; + } else if (key_kind == CERT) { + isc_throw(UnsupportedAlgorithm, + "Unknown Certificate format: " << + static_cast(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << + static_cast(key_kind)); + } +} + +/// @brief Export the key value (file) +void RsaAsymImpl::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(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(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(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\n"); + if (hash_ == MD5) { + fprintf(fp, "Algorithm: 1 (RSA)\n"); + } else if (hash_ == SHA1) { + fprintf(fp, "Algorithm: 5 (RSASHA1)\n"); + } else if (hash_ == SHA256) { + fprintf(fp, "Algorithm: 8 (RSASHA256)\n"); + } else if (hash_ == SHA512) { + fprintf(fp, "Algorithm: 10 (RSASHA512)\n"); + } + std::vector bin; + bin.resize(BN_num_bytes(rsa->n)); + BN_bn2bin(rsa->n, &bin[0]); + fprintf(fp, "Modulus: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->e)); + BN_bn2bin(rsa->e, &bin[0]); + fprintf(fp, "PublicExponent: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->d)); + BN_bn2bin(rsa->d, &bin[0]); + fprintf(fp, "PrivateExponent: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->p)); + BN_bn2bin(rsa->p, &bin[0]); + fprintf(fp, "Prime1: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->q)); + BN_bn2bin(rsa->q, &bin[0]); + fprintf(fp, "Prime2: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->dmp1)); + BN_bn2bin(rsa->dmp1, &bin[0]); + fprintf(fp, "Exponent1: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->dmq1)); + BN_bn2bin(rsa->dmq1, &bin[0]); + fprintf(fp, "Exponent2: %s\n", + util::encode::encodeBase64(bin).c_str()); + bin.resize(BN_num_bytes(rsa->iqmp)); + BN_bn2bin(rsa->iqmp, &bin[0]); + fprintf(fp, "Coefficient: %s\n", + 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(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(), "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"); + } + 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(), "w"); + 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 bin = exportkey(key_kind, key_format); + FILE* fp = fopen(filename.c_str(), "w"); + 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(key_format)); + } else if ((key_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(), "w"); + 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 (key_kind == CERT) { + if (!x509_) { + isc_throw(UnsupportedAlgorithm, "Have no Certificate"); + } + isc_throw(UnsupportedAlgorithm, + "Unknown Certificate format: " << + static_cast(key_format)); + } else { + isc_throw(UnsupportedAlgorithm, + "Unknown RSA Key kind: " << + static_cast(key_kind)); + } +} + +/// @brief Check the validity +bool RsaAsymImpl::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 +bool RsaAsymImpl::compare(const RsaAsymImpl* other, + const AsymKeyKind key_kind) const { + if (!other || (other->algo_ != RSA_)) { + return false; + } + 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; + } +} + +} // namespace cryptolink +} // namespace isc diff --git a/src/lib/cryptolink/openssl_rsa.h b/src/lib/cryptolink/openssl_rsa.h new file mode 100644 index 0000000000..9b675e0a9f --- /dev/null +++ b/src/lib/cryptolink/openssl_rsa.h @@ -0,0 +1,148 @@ +// 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 +// 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. + +namespace isc { +namespace cryptolink { + +/// @brief OpenSSL implementation of asymmetrical cryptography (Asym). +// Each method is the counterpart of the Asym corresponding method. +class RsaAsymImpl : public 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 hash_algorithm The hash algorithm + /// @param key_kind The key kind + /// @param key_format The key binary format + RsaAsymImpl(const void* key, size_t key_len, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format); + + /// @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 hash_algorithm The hash algorithm + /// @param key_kind The key kind + /// @param key_format The key binary format + RsaAsymImpl(const std::string& filename, + const std::string& password, + const HashAlgorithm hash_algorithm, + const AsymKeyKind key_kind, + const AsymFormat key_format); + + /// @brief Destructor + virtual ~RsaAsymImpl(); + + /// @brief Returns the AsymAlgorithm of the object + AsymAlgorithm getAsymAlgorithm() const; + + /// @brief Returns the HashAlgorithm of the object + HashAlgorithm getHashAlgorithm() const; + + /// @brief Returns the AsymKeyKind of the object + AsymKeyKind getAsymKeyKind() const; + + /// @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 + /// + /// See @ref isc::cryptolink::AsymBase::update() for details. + void update(const void* data, const size_t len); + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::AsymBase::sign() for details. + void sign(isc::util::OutputBuffer& result, size_t len, + const AsymFormat sig_format); + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::AsymBase::sign() for details. + void sign(void* result, size_t len, const AsymFormat sig_format); + + /// @brief Calculate the final signature + /// + /// See @ref isc::cryptolink::AsymBase::sign() for details. + std::vector sign(size_t len, const AsymFormat sig_format); + + /// @brief Verify an existing signature + /// + /// See @ref isc::cryptolink::AsymBase::verify() for details. + bool verify(const void* sig, size_t len, const AsymFormat sig_format); + + /// @brief Clear the crypto state and go back to the initial state + /// (must be called before reusing an Asym object) + void clear(); + + /// @brief Export the key value (binary) + /// + /// See @ref isc::cryptolink::AsymBase::exportkey() for details + std::vector exportkey(const AsymKeyKind key_kind, + const AsymFormat key_format) const; + + /// @brief Export the key value (file) + /// + /// See @ref isc::cryptolink::AsymBase::exportkey() for details + void exportkey(const std::string& filename, + const std::string& password, + const AsymKeyKind key_kind, + const AsymFormat key_format) const; + + /// @brief Check the validity + /// + /// See @ref isc::cryptolink::AsymBase::validate() for details + bool validate() const; + + /// @brief Compare two keys + /// + /// See @ref isc::cryptolink::Asym::compare() for details + bool compare(const RsaAsymImpl* other, const AsymKeyKind key_kind) const; + +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 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_; +}; + +} // namespace cryptolink +} // namespace isc -- 2.47.2