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
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}
#include <cryptolink.h>
#include <cryptolink/crypto_asym.h>
-#include <boost/scoped_ptr.hpp>
-
#include <botan/version.h>
#include <botan/botan.h>
-#include <botan/hash.h>
-#include <botan/oids.h>
#include <botan/pubkey.h>
#include <botan/rsa.h>
#include <botan/x509stor.h>
-#include <util/encode/base64.h>
#include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h>
-#include <cryptolink/botan_common.h>
-
-#include <cstring>
-#include <fstream>
+#include <cryptolink/botan_rsa.h>
namespace {
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<int>(hash_));
- }
- std::string emsa = "EMSA3(" + hash + ")";
- if (key_len == 0) {
- isc_throw(BadKey, "Bad RSA " <<
- (kind_ != CERT ? "key" : "cert") <<
- " length: 0");
- }
-
- if ((kind_ == PRIVATE) && (key_format == BASIC)) {
- // PKCS#1 Private Key
- const Botan::byte* keyin =
- reinterpret_cast<const Botan::byte*>(key);
- const Botan::MemoryVector<Botan::byte> ber(keyin, key_len);
- // rsaEncription OID and NULL parameters
- const Botan::AlgorithmIdentifier alg_id =
- Botan::AlgorithmIdentifier("1.2.840.113549.1.1.1",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- Botan::AutoSeeded_RNG rng;
- try {
- priv_.reset(new Botan::RSA_PrivateKey(alg_id, ber, rng));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "RSA_PrivateKey: " << exc.what());
- }
- } else if (kind_ == PRIVATE) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Private Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
- // PKCS#1 Public Key
- const Botan::byte* keyin =
- reinterpret_cast<const Botan::byte*>(key);
- const Botan::MemoryVector<Botan::byte> ber(keyin, key_len);
- // rsaEncription OID and NULL parameters
- const Botan::AlgorithmIdentifier alg_id =
- Botan::AlgorithmIdentifier("1.2.840.113549.1.1.1",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- try {
- pub_.reset(new Botan::RSA_PublicKey(alg_id, ber));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
- }
- } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo
- const Botan::byte* keyin =
- reinterpret_cast<const Botan::byte*>(key);
- Botan::DataSource_Memory source(keyin, key_len);
- Botan::Public_Key* key;
- try {
- key = Botan::X509::load_key(source);
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "X509::load_key: " << exc.what());
- }
- if (key->algo_name().compare("RSA") != 0) {
- delete key;
- isc_throw(BadKey, "not a RSA Public Key");
- }
- pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
- if (!pub_) {
- delete key;
- isc_throw(LibraryError, "dynamic_cast");
- }
- } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
- // RFC 3110 DNS wire format
- // key_len == 0 was already checked
- const uint8_t* p =
- reinterpret_cast<const uint8_t*>(key);
- size_t e_bytes = *p++;
- --key_len;
- if (e_bytes == 0) {
- if (key_len < 2) {
- isc_throw(BadKey,
- "Bad RSA Public Key: short exponent length");
- }
- e_bytes = (*p++) << 8;
- e_bytes += *p++;
- key_len -= 2;
- }
- if (key_len < e_bytes) {
- isc_throw(BadKey, "Bad RSA Public Key: short exponent");
- }
- if ((key_len - e_bytes) < 64) {
- isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
- (key_len - e_bytes) * 8);
- }
- if ((key_len - e_bytes) > 512) {
- isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
- (key_len - e_bytes) * 8);
- }
- Botan::BigInt e(p, e_bytes);
- p += e_bytes;
- key_len -= e_bytes;
- Botan::BigInt n(p, key_len);
- try {
- pub_.reset(new Botan::RSA_PublicKey(n, e));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
- }
- } else if (kind_ == PUBLIC) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Public Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == CERT) && (key_format == ASN1)) {
- // X.509 Public Key Certificate
- const Botan::byte* keyin =
- reinterpret_cast<const Botan::byte*>(key);
- Botan::DataSource_Memory source(keyin, key_len);
- try {
- x509_.reset(new Botan::X509_Certificate(source));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "X509_Certificate: " << exc.what());
- }
- const Botan::AlgorithmIdentifier
- sig_algo(x509_->signature_algorithm());
- if (hash_ == MD5) {
- const Botan::AlgorithmIdentifier
- rsa_md5("1.2.840.113549.1.1.4",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_md5) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA MD5 certificate");
- }
- } else if (hash_ == SHA1) {
- const Botan::AlgorithmIdentifier
- rsa_sha1("1.2.840.113549.1.1.5",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha1) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA1 certificate");
- }
- } else if (hash_ == SHA224) {
- const Botan::AlgorithmIdentifier
- rsa_sha224("1.2.840.113549.1.1.14",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha224) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA224 certificate");
- }
- } else if (hash_ == SHA256) {
- const Botan::AlgorithmIdentifier
- rsa_sha256("1.2.840.113549.1.1.11",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha256) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA256 certificate");
- }
- } else if (hash_ == SHA384) {
- const Botan::AlgorithmIdentifier
- rsa_sha384("1.2.840.113549.1.1.12",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha384) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA384 certificate");
- }
- } else if (hash_ == SHA512) {
- const Botan::AlgorithmIdentifier
- rsa_sha512("1.2.840.113549.1.1.13",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha512) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA512 certificate");
- }
- } else {
- x509_.reset();
- isc_throw(UnsupportedAlgorithm,
- "Bad hash algorithm for certificate: " <<
- static_cast<int>(hash_));
- }
- Botan::Public_Key* key = x509_->subject_public_key();
- if (key->algo_name().compare("RSA") != 0) {
- delete key;
- x509_.reset();
- isc_throw(BadKey, "not a RSA Certificate");
- }
- pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
- if (!pub_) {
- delete key;
- x509_.reset();
- isc_throw(LibraryError, "dynamic_cast");
- }
- } else if (kind_ == CERT) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown Certificate format: " <<
- static_cast<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(kind_));
- }
-
- if (kind_ == PRIVATE) {
- try {
- 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<int>(hash_));
- }
- std::string emsa = "EMSA3(" + hash + ")";
-
- if ((kind_ == PRIVATE) && (key_format == ASN1)) {
- // PKCS#8 Private Key PEM file
- Botan::Private_Key* key;
- Botan::AutoSeeded_RNG rng;
- try {
- key = Botan::PKCS8::load_key(filename, rng, password);
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "PKCS8::load_key: " << exc.what());
- }
- if (key->algo_name().compare("RSA") != 0) {
- delete key;
- isc_throw(BadKey, "not a RSA Private Key");
- }
- priv_.reset(dynamic_cast<Botan::RSA_PrivateKey*>(key));
- if (!priv_) {
- delete key;
- isc_throw(LibraryError, "dynamic_cast");
- }
- } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
- // bind9 .private file
- // warn when password not empty
- if ((hash_ != MD5) && (hash_ != SHA1) &&
- (hash_ != SHA256) && (hash_ != SHA512)) {
- isc_throw(UnsupportedAlgorithm,
- "Not compatible hash algorithm: " <<
- static_cast<int>(hash_));
- }
- std::ifstream fp(filename.c_str(), std::ios::in);
- if (!fp.is_open()) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- bool got_algorithm = false;
- bool got_modulus = false;
- bool got_pub_exponent = false;
- bool got_priv_exponent = false;
- bool got_prime1 = false;
- bool got_prime2 = false;
- Botan::BigInt n;
- Botan::BigInt e;
- Botan::BigInt d;
- Botan::BigInt p;
- Botan::BigInt q;
- while (fp.good()) {
- std::string line;
- getline(fp, line);
- if (line.find("Algorithm:") == 0) {
- if (got_algorithm) {
- fp.close();
- isc_throw(BadKey, "Two Algorithm entries");
- }
- got_algorithm = true;
- std::string value = line.substr(strlen("Algorithm:") + 1,
- std::string::npos);
- int alg = std::atoi(value.c_str());
- if (alg == 1) {
- // RSAMD5
- if (hash_ != MD5) {
- fp.close();
- isc_throw(BadKey, "Require a RSA MD5 key");
- }
- } else if ((alg == 5) || (alg == 7)) {
- // RSASHA1 or RSASHA1-NSEC3-SHA1
- if (hash_ != SHA1) {
- fp.close();
- isc_throw(BadKey, "Require a RSA SHA1 key");
- }
- } else if (alg == 8) {
- // RSASHA256
- if (hash_ != SHA256) {
- fp.close();
- isc_throw(BadKey, "Require a RSA SHA256 key");
- }
- } else if (alg == 10) {
- // RSASHA512
- if (hash_ != SHA512) {
- fp.close();
- isc_throw(BadKey, "Require a RSA SHA512 key");
- }
- } else {
- fp.close();
- isc_throw(BadKey, "Bad Algorithm: " << alg);
- }
- } else if (line.find("Modulus:") == 0) {
- if (got_modulus) {
- fp.close();
- isc_throw(BadKey, "Two Modulus entries");
- }
- got_modulus = true;
- std::string value = line.substr(strlen("Modulus:") + 1,
- std::string::npos);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- fp.close();
- isc_throw(BadKey, "Modulus: " << exc.what());
- }
- n = Botan::BigInt(&bin[0], bin.size());
- } else if (line.find("PublicExponent:") == 0) {
- if (got_pub_exponent) {
- fp.close();
- isc_throw(BadKey, "Two PublicExponent entries");
- }
- got_pub_exponent = true;
- std::string value =
- line.substr(strlen("PublicExponent:") + 1,
- std::string::npos);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- fp.close();
- isc_throw(BadKey, "PublicExponent: " << exc.what());
- }
- e = Botan::BigInt(&bin[0], bin.size());
- } else if (line.find("PrivateExponent:") == 0) {
- if (got_priv_exponent) {
- fp.close();
- isc_throw(BadKey, "Two PrivateExponent entries");
- }
- got_priv_exponent = true;
- std::string value =
- line.substr(strlen("PrivateExponent:") + 1,
- std::string::npos);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- fp.close();
- isc_throw(BadKey, "PrivateExponent: " << exc.what());
- }
- d = Botan::BigInt(&bin[0], bin.size());
- } else if (line.find("Prime1:") == 0) {
- if (got_prime1) {
- fp.close();
- isc_throw(BadKey, "Two Prime1 entries");
- }
- got_prime1 = true;
- std::string value = line.substr(strlen("Prime1:") + 1,
- std::string::npos);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- fp.close();
- isc_throw(BadKey, "Prime1: " << exc.what());
- }
- p = Botan::BigInt(&bin[0], bin.size());
- } else if (line.find("Prime2:") == 0) {
- if (got_prime2) {
- fp.close();
- isc_throw(BadKey, "Two Prime2 entries");
- }
- got_prime2 = true;
- std::string value = line.substr(strlen("Prime2:") + 1,
- std::string::npos);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- fp.close();
- isc_throw(BadKey, "Prime2: " << exc.what());
- }
- q = Botan::BigInt(&bin[0], bin.size());
- }
- }
- fp.close();
- if (!got_algorithm) {
- isc_throw(BadKey, "Missing Algorithm entry");
- }
- if (!got_modulus) {
- isc_throw(BadKey, "Missing Modulus entry");
- }
- if (!got_pub_exponent) {
- isc_throw(BadKey, "Missing PublicExponent entry");
- }
- if (!got_priv_exponent) {
- isc_throw(BadKey, "Missing PrivateExponent entry");
- }
- if (!got_prime1) {
- isc_throw(BadKey, "Missing Prime1 entry");
- }
- if (!got_prime2) {
- isc_throw(BadKey, "Missing Prime2 entry");
- }
- Botan::AutoSeeded_RNG rng;
- try {
- priv_.reset(new Botan::RSA_PrivateKey(rng, p, q, e, d, n));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "RSA_PrivateKey" << exc.what());
- }
- } else if (kind_ == PRIVATE) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Private Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo PEM file
- // warn when password not empty
- Botan::Public_Key* key;
- try {
- key = Botan::X509::load_key(filename);
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "X509::load_key: " << exc.what());
- }
- if (key->algo_name().compare("RSA") != 0) {
- delete key;
- isc_throw(BadKey, "not a RSA Public Key");
- }
- pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
- if (!pub_) {
- delete key;
- isc_throw(LibraryError, "dynamic_cast");
- }
- } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
- // bind9 .key file (RDATA)
- // warn when password not empty
- if ((hash_ != MD5) && (hash_ != SHA1) &&
- (hash_ != SHA256) && (hash_ != SHA512)) {
- isc_throw(UnsupportedAlgorithm,
- "Not compatible hash algorithm: " <<
- static_cast<int>(hash_));
- }
- std::ifstream fp(filename.c_str(), std::ios::in);
- if (!fp.is_open()) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- std::string line;
- bool found = false;
- while (fp.good()) {
- getline(fp, line);
- if (line.empty() || (line[0] == ';')) {
- continue;
- }
- if (line.find("DNSKEY") == std::string::npos) {
- continue;
- }
- found = true;
- if (line[line.size() - 1] == '\n') {
- line.erase(line.size() - 1);
- }
- break;
- }
- fp.close();
- if (!found) {
- isc_throw(BadKey, "Can't find a DNSKEY");
- }
- const std::string b64 =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
- "ghijklmnopqrstuvwxyz0123456789+/=";
- const std::string value = line.substr(line.find_last_not_of(b64));
- std::vector<uint8_t> bin;
- try {
- util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- isc_throw(BadKey, "Can't decode base64: " << exc.what());
- }
- size_t key_len = bin.size();
- const uint8_t* p = &bin[0];
- size_t e_bytes = *p++;
- --key_len;
- if (e_bytes == 0) {
- if (key_len < 2) {
- isc_throw(BadKey,
- "Bad RSA Public Key: short exponent length");
- }
- e_bytes = (*p++) << 8;
- e_bytes += *p++;
- key_len -= 2;
- }
- if (key_len < e_bytes) {
- isc_throw(BadKey, "Bad RSA Public Key: short exponent");
- }
- if ((key_len - e_bytes) < 64) {
- isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
- (key_len - e_bytes) * 8);
- }
- if ((key_len - e_bytes) > 512) {
- isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
- (key_len - e_bytes) * 8);
- }
- Botan::BigInt e(p, e_bytes);
- p += e_bytes;
- key_len -= e_bytes;
- Botan::BigInt n(p, key_len);
- try {
- pub_.reset(new Botan::RSA_PublicKey(n, e));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
- }
- } else if (kind_ == PUBLIC) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Public Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == CERT) && (key_format == ASN1)) {
- // X.509 Public Key Certificate PEM file
- // warn when password not empty
- try {
- x509_.reset(new Botan::X509_Certificate(filename));
- } catch (const std::exception& exc) {
- isc_throw(BadKey, "X509_Certificate: " << exc.what());
- }
- const Botan::AlgorithmIdentifier
- sig_algo(x509_->signature_algorithm());
- if (hash_ == MD5) {
- const Botan::AlgorithmIdentifier
- rsa_md5("1.2.840.113549.1.1.4",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_md5) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA MD5 certificate");
- }
- } else if (hash_ == SHA1) {
- const Botan::AlgorithmIdentifier
- rsa_sha1("1.2.840.113549.1.1.5",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha1) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA1 certificate");
- }
- } else if (hash_ == SHA224) {
- const Botan::AlgorithmIdentifier
- rsa_sha224("1.2.840.113549.1.1.14",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha224) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA224 certificate");
- }
- } else if (hash_ == SHA256) {
- const Botan::AlgorithmIdentifier
- rsa_sha256("1.2.840.113549.1.1.11",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha256) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA256 certificate");
- }
- } else if (hash_ == SHA384) {
- const Botan::AlgorithmIdentifier
- rsa_sha384("1.2.840.113549.1.1.12",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha384) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA384 certificate");
- }
- } else if (hash_ == SHA512) {
- const Botan::AlgorithmIdentifier
- rsa_sha512("1.2.840.113549.1.1.13",
- Botan::AlgorithmIdentifier::USE_NULL_PARAM);
- if (sig_algo != rsa_sha512) {
- x509_.reset();
- isc_throw(BadKey, "Require a RSA SHA512 certificate");
- }
- } else {
- x509_.reset();
- isc_throw(UnsupportedAlgorithm,
- "Bad hash algorithm for certificate: " <<
- static_cast<int>(hash_));
- }
- Botan::Public_Key* key;
- try {
- key = x509_->subject_public_key();
- } catch (const std::exception& exc) {
- x509_.reset();
- isc_throw(BadKey, "subject_public_key: " << exc.what());
- }
- if (key->algo_name().compare("RSA") != 0) {
- delete key;
- x509_.reset();
- isc_throw(BadKey, "not a RSA Public Key");
- }
- pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
- if (!pub_) {
- delete key;
- x509_.reset();
- isc_throw(LibraryError, "dynamic_cast");
- }
- } else if (kind_ == CERT) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown Public Key Certificate format: " <<
- static_cast<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(kind_));
- }
-
- if (kind_ == PRIVATE) {
- try {
- 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<int>(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<const Botan::byte*>(data), len);
- } else {
- verifier_->update(
- reinterpret_cast<const Botan::byte*>(data), len);
- }
- } catch (const std::exception& exc) {
- isc_throw(LibraryError, "update: " << exc.what());
- }
- }
-
- /// @brief Calculate the final signature
- ///
- /// See @ref isc::cryptolink::AsymBase::sign() for details.
- void sign(isc::util::OutputBuffer& result, size_t len,
- const AsymFormat) {
- try {
- Botan::SecureVector<Botan::byte> b_result;
- Botan::AutoSeeded_RNG rng;
- b_result = signer_->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<Botan::byte> 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<uint8_t> sign(size_t len, const AsymFormat) {
- try {
- Botan::SecureVector<Botan::byte> b_result;
- Botan::AutoSeeded_RNG rng;
- b_result = signer_->signature(rng);
- if (len > b_result.size()) {
- return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
- } else {
- return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
- }
- } catch (const std::exception& exc) {
- isc_throw(LibraryError, "signature: " << exc.what());
- }
- }
-
- /// @brief Verify an existing signature
- ///
- /// See @ref isc::cryptolink::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<const Botan::byte*>(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<int>(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<uint8_t> exportkey(const AsymKeyKind key_kind,
- const AsymFormat key_format) const {
- if ((key_kind == PRIVATE) && (key_format == BASIC)) {
- // PKCS#1 Private Key
- if (kind_ != PRIVATE) {
- isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
- }
- Botan::MemoryVector<Botan::byte> der;
- try {
- der = priv_->pkcs8_private_key();
- } catch (const std::exception& exc) {
- isc_throw(LibraryError, "pkcs8_private_key: " << exc.what());
- }
- return std::vector<uint8_t>(der.begin(), der.end());
- } else if (key_kind == PRIVATE) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Private Key format: " <<
- static_cast<int>(key_format));
- } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
- // PKCS#1 Public Key
- Botan::MemoryVector<Botan::byte> der;
- try {
- der = pub_->x509_subject_public_key();
- } catch (const std::exception& exc) {
- isc_throw(LibraryError,
- "x509_subject_public_key: "
- << exc.what());
- }
- return std::vector<uint8_t>(der.begin(), der.end());
- } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo
- Botan::MemoryVector<Botan::byte> ber;
- try {
- ber = Botan::X509::BER_encode(*pub_);
- } catch (const std::exception& exc) {
- isc_throw(LibraryError, "X509::BER_encode: " << exc.what());
- }
- return std::vector<uint8_t>(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<uint8_t> rdata(x_bytes + e_bytes + mod_bytes);
- if (e_bytes < 256) {
- rdata[0] = e_bytes;
- } else {
- rdata[0] = 0;
- rdata[1] = (e_bytes >> 8) & 0xff;
- rdata[2] = e_bytes & 0xff;
- }
- pub_->get_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<int>(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<Botan::byte> ber;
- try {
- ber = x509_->BER_encode();
- } catch (const std::exception& exc) {
- isc_throw(LibraryError, "BER_encode" << exc.what());
- }
- return std::vector<uint8_t>(ber.begin(), ber.end());
- } else if (key_kind == CERT) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown Certificate format: " <<
- static_cast<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(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<int>(hash_));
- }
- std::ofstream fp(filename.c_str(),
- std::ofstream::out | std::ofstream::trunc);
- if (!fp.is_open()) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- fp << "Private-key-format: v1.2\n";
- if (hash_ == MD5) {
- fp << "Algorithm: 1 (RSA)\n";
- } else if (hash_ == SHA1) {
- fp << "Algorithm: 5 (RSASHA1)\n";
- } else if (hash_ == SHA256) {
- fp << "Algorithm: 8 (RSASHA256)\n";
- } else if (hash_ == SHA512) {
- fp << "Algorithm: 10 (RSASHA512)\n";
- }
- std::vector<uint8_t> bin;
- bin.resize(priv_->get_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<int>(key_format));
- } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo PEM file
- // warn when password not empty
- std::string pem;
- try {
- pem = Botan::X509::PEM_encode(*pub_);
- } catch (const std::exception& exc) {
- isc_throw(LibraryError, "X509::PEM_encode: " << exc.what());
- }
- std::ofstream fp(filename.c_str(),
- std::ofstream::out | std::ofstream::trunc);
- if (fp.is_open()) {
- fp.write(pem.c_str(), pem.size());
- fp.close();
- } else {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- } else if ((key_kind == PUBLIC) && (key_format == DNS)) {
- // bind9 .key file (RDATA)
- // warn when password not empty
- std::vector<uint8_t> bin = exportkey(key_kind, key_format);
- std::ofstream fp(filename.c_str(),
- std::ofstream::out | std::ofstream::trunc);
- if (!fp.is_open()) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- fp << "; DNSKEY RDATA: " <<
- util::encode::encodeBase64(bin) << '\n';
- fp.close();
- } else if (key_kind == PUBLIC) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Public Key format: " <<
- static_cast<int>(key_format));
- } else if ((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<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(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<Botan::RSA_PrivateKey> priv_;
- /// @brief The protected pointer to the Botan public key
- boost::scoped_ptr<Botan::RSA_PublicKey> pub_;
- /// @brief The protected pointer to the Botan certificate
- boost::scoped_ptr<Botan::X509_Certificate> x509_;
- /// @brief The protected pointer to the Botan signer
- boost::scoped_ptr<Botan::PK_Signer> signer_;
- /// @brief The protected pointer to the Botan verifier
- boost::scoped_ptr<Botan::PK_Verifier> verifier_;
-};
-
Asym::Asym(const void* key, size_t key_len,
const AsymAlgorithm asym_algorithm,
const HashAlgorithm hash_algorithm,
--- /dev/null
+// 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 <cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <botan/version.h>
+#include <botan/botan.h>
+#include <botan/hash.h>
+#include <botan/oids.h>
+#include <botan/pubkey.h>
+#include <botan/rsa.h>
+#include <botan/x509stor.h>
+
+#include <util/encode/base64.h>
+#include <cryptolink/botan_common.h>
+#include <cryptolink/botan_rsa.h>
+
+#include <cstring>
+#include <fstream>
+
+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<int>(hash_));
+ }
+ std::string emsa = "EMSA3(" + hash + ")";
+ if (key_len == 0) {
+ isc_throw(BadKey, "Bad RSA " <<
+ (kind_ != CERT ? "key" : "cert") << " length: 0");
+ }
+
+ if ((kind_ == PRIVATE) && (key_format == BASIC)) {
+ // PKCS#1 Private Key
+ const Botan::byte* keyin = reinterpret_cast<const Botan::byte*>(key);
+ const Botan::MemoryVector<Botan::byte> ber(keyin, key_len);
+ // rsaEncription OID and NULL parameters
+ const Botan::AlgorithmIdentifier alg_id =
+ Botan::AlgorithmIdentifier("1.2.840.113549.1.1.1",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ Botan::AutoSeeded_RNG rng;
+ try {
+ priv_.reset(new Botan::RSA_PrivateKey(alg_id, ber, rng));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "RSA_PrivateKey: " << exc.what());
+ }
+ } else if (kind_ == PRIVATE) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Private Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
+ // PKCS#1 Public Key
+ const Botan::byte* keyin = reinterpret_cast<const Botan::byte*>(key);
+ const Botan::MemoryVector<Botan::byte> ber(keyin, key_len);
+ // rsaEncription OID and NULL parameters
+ const Botan::AlgorithmIdentifier alg_id =
+ Botan::AlgorithmIdentifier("1.2.840.113549.1.1.1",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ try {
+ pub_.reset(new Botan::RSA_PublicKey(alg_id, ber));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
+ }
+ } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo
+ const Botan::byte* keyin = reinterpret_cast<const Botan::byte*>(key);
+ Botan::DataSource_Memory source(keyin, key_len);
+ Botan::Public_Key* key;
+ try {
+ key = Botan::X509::load_key(source);
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "X509::load_key: " << exc.what());
+ }
+ if (key->algo_name().compare("RSA") != 0) {
+ delete key;
+ isc_throw(BadKey, "not a RSA Public Key");
+ }
+ pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+ if (!pub_) {
+ delete key;
+ isc_throw(LibraryError, "dynamic_cast");
+ }
+ } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+ // RFC 3110 DNS wire format
+ // key_len == 0 was already checked
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(key);
+ size_t e_bytes = *p++;
+ --key_len;
+ if (e_bytes == 0) {
+ if (key_len < 2) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent length");
+ }
+ e_bytes = (*p++) << 8;
+ e_bytes += *p++;
+ key_len -= 2;
+ }
+ if (key_len < e_bytes) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+ }
+ if ((key_len - e_bytes) < 64) {
+ isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+ (key_len - e_bytes) * 8);
+ }
+ if ((key_len - e_bytes) > 512) {
+ isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+ (key_len - e_bytes) * 8);
+ }
+ Botan::BigInt e(p, e_bytes);
+ p += e_bytes;
+ key_len -= e_bytes;
+ Botan::BigInt n(p, key_len);
+ try {
+ pub_.reset(new Botan::RSA_PublicKey(n, e));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
+ }
+ } else if (kind_ == PUBLIC) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Public Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == CERT) && (key_format == ASN1)) {
+ // X.509 Public Key Certificate
+ const Botan::byte* keyin = reinterpret_cast<const Botan::byte*>(key);
+ Botan::DataSource_Memory source(keyin, key_len);
+ try {
+ x509_.reset(new Botan::X509_Certificate(source));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "X509_Certificate: " << exc.what());
+ }
+ const Botan::AlgorithmIdentifier
+ sig_algo(x509_->signature_algorithm());
+ if (hash_ == MD5) {
+ const Botan::AlgorithmIdentifier
+ rsa_md5("1.2.840.113549.1.1.4",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_md5) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA MD5 certificate");
+ }
+ } else if (hash_ == SHA1) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha1("1.2.840.113549.1.1.5",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha1) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA1 certificate");
+ }
+ } else if (hash_ == SHA224) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha224("1.2.840.113549.1.1.14",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha224) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA224 certificate");
+ }
+ } else if (hash_ == SHA256) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha256("1.2.840.113549.1.1.11",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha256) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA256 certificate");
+ }
+ } else if (hash_ == SHA384) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha384("1.2.840.113549.1.1.12",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha384) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA384 certificate");
+ }
+ } else if (hash_ == SHA512) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha512("1.2.840.113549.1.1.13",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha512) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA512 certificate");
+ }
+ } else {
+ x509_.reset();
+ isc_throw(UnsupportedAlgorithm,
+ "Bad hash algorithm for certificate: " <<
+ static_cast<int>(hash_));
+ }
+ Botan::Public_Key* key = x509_->subject_public_key();
+ if (key->algo_name().compare("RSA") != 0) {
+ delete key;
+ x509_.reset();
+ isc_throw(BadKey, "not a RSA Certificate");
+ }
+ pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+ if (!pub_) {
+ delete key;
+ x509_.reset();
+ isc_throw(LibraryError, "dynamic_cast");
+ }
+ } else if (kind_ == CERT) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown Certificate format: " <<
+ static_cast<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " << static_cast<int>(kind_));
+ }
+
+ if (kind_ == PRIVATE) {
+ try {
+ 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<int>(hash_));
+ }
+ std::string emsa = "EMSA3(" + hash + ")";
+
+ if ((kind_ == PRIVATE) && (key_format == ASN1)) {
+ // PKCS#8 Private Key PEM file
+ Botan::Private_Key* key;
+ Botan::AutoSeeded_RNG rng;
+ try {
+ key = Botan::PKCS8::load_key(filename, rng, password);
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "PKCS8::load_key: " << exc.what());
+ }
+ if (key->algo_name().compare("RSA") != 0) {
+ delete key;
+ isc_throw(BadKey, "not a RSA Private Key");
+ }
+ priv_.reset(dynamic_cast<Botan::RSA_PrivateKey*>(key));
+ if (!priv_) {
+ delete key;
+ isc_throw(LibraryError, "dynamic_cast");
+ }
+ } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
+ // bind9 .private file
+ // warn when password not empty
+ if ((hash_ != MD5) && (hash_ != SHA1) &&
+ (hash_ != SHA256) && (hash_ != SHA512)) {
+ isc_throw(UnsupportedAlgorithm,
+ "Not compatible hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ std::ifstream fp(filename.c_str(), std::ios::in);
+ if (!fp.is_open()) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ bool got_algorithm = false;
+ bool got_modulus = false;
+ bool got_pub_exponent = false;
+ bool got_priv_exponent = false;
+ bool got_prime1 = false;
+ bool got_prime2 = false;
+ Botan::BigInt n;
+ Botan::BigInt e;
+ Botan::BigInt d;
+ Botan::BigInt p;
+ Botan::BigInt q;
+ while (fp.good()) {
+ std::string line;
+ getline(fp, line);
+ if (line.find("Algorithm:") == 0) {
+ if (got_algorithm) {
+ fp.close();
+ isc_throw(BadKey, "Two Algorithm entries");
+ }
+ got_algorithm = true;
+ std::string value = line.substr(strlen("Algorithm:") + 1,
+ std::string::npos);
+ int alg = std::atoi(value.c_str());
+ if (alg == 1) {
+ // RSAMD5
+ if (hash_ != MD5) {
+ fp.close();
+ isc_throw(BadKey, "Require a RSA MD5 key");
+ }
+ } else if ((alg == 5) || (alg == 7)) {
+ // RSASHA1 or RSASHA1-NSEC3-SHA1
+ if (hash_ != SHA1) {
+ fp.close();
+ isc_throw(BadKey, "Require a RSA SHA1 key");
+ }
+ } else if (alg == 8) {
+ // RSASHA256
+ if (hash_ != SHA256) {
+ fp.close();
+ isc_throw(BadKey, "Require a RSA SHA256 key");
+ }
+ } else if (alg == 10) {
+ // RSASHA512
+ if (hash_ != SHA512) {
+ fp.close();
+ isc_throw(BadKey, "Require a RSA SHA512 key");
+ }
+ } else {
+ fp.close();
+ isc_throw(BadKey, "Bad Algorithm: " << alg);
+ }
+ } else if (line.find("Modulus:") == 0) {
+ if (got_modulus) {
+ fp.close();
+ isc_throw(BadKey, "Two Modulus entries");
+ }
+ got_modulus = true;
+ std::string value = line.substr(strlen("Modulus:") + 1,
+ std::string::npos);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ fp.close();
+ isc_throw(BadKey, "Modulus: " << exc.what());
+ }
+ n = Botan::BigInt(&bin[0], bin.size());
+ } else if (line.find("PublicExponent:") == 0) {
+ if (got_pub_exponent) {
+ fp.close();
+ isc_throw(BadKey, "Two PublicExponent entries");
+ }
+ got_pub_exponent = true;
+ std::string value =
+ line.substr(strlen("PublicExponent:") + 1,
+ std::string::npos);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ fp.close();
+ isc_throw(BadKey, "PublicExponent: " << exc.what());
+ }
+ e = Botan::BigInt(&bin[0], bin.size());
+ } else if (line.find("PrivateExponent:") == 0) {
+ if (got_priv_exponent) {
+ fp.close();
+ isc_throw(BadKey, "Two PrivateExponent entries");
+ }
+ got_priv_exponent = true;
+ std::string value =
+ line.substr(strlen("PrivateExponent:") + 1,
+ std::string::npos);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ fp.close();
+ isc_throw(BadKey, "PrivateExponent: " << exc.what());
+ }
+ d = Botan::BigInt(&bin[0], bin.size());
+ } else if (line.find("Prime1:") == 0) {
+ if (got_prime1) {
+ fp.close();
+ isc_throw(BadKey, "Two Prime1 entries");
+ }
+ got_prime1 = true;
+ std::string value = line.substr(strlen("Prime1:") + 1,
+ std::string::npos);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ fp.close();
+ isc_throw(BadKey, "Prime1: " << exc.what());
+ }
+ p = Botan::BigInt(&bin[0], bin.size());
+ } else if (line.find("Prime2:") == 0) {
+ if (got_prime2) {
+ fp.close();
+ isc_throw(BadKey, "Two Prime2 entries");
+ }
+ got_prime2 = true;
+ std::string value = line.substr(strlen("Prime2:") + 1,
+ std::string::npos);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ fp.close();
+ isc_throw(BadKey, "Prime2: " << exc.what());
+ }
+ q = Botan::BigInt(&bin[0], bin.size());
+ }
+ }
+ fp.close();
+ if (!got_algorithm) {
+ isc_throw(BadKey, "Missing Algorithm entry");
+ }
+ if (!got_modulus) {
+ isc_throw(BadKey, "Missing Modulus entry");
+ }
+ if (!got_pub_exponent) {
+ isc_throw(BadKey, "Missing PublicExponent entry");
+ }
+ if (!got_priv_exponent) {
+ isc_throw(BadKey, "Missing PrivateExponent entry");
+ }
+ if (!got_prime1) {
+ isc_throw(BadKey, "Missing Prime1 entry");
+ }
+ if (!got_prime2) {
+ isc_throw(BadKey, "Missing Prime2 entry");
+ }
+ Botan::AutoSeeded_RNG rng;
+ try {
+ priv_.reset(new Botan::RSA_PrivateKey(rng, p, q, e, d, n));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "RSA_PrivateKey" << exc.what());
+ }
+ } else if (kind_ == PRIVATE) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Private Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo PEM file
+ // warn when password not empty
+ Botan::Public_Key* key;
+ try {
+ key = Botan::X509::load_key(filename);
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "X509::load_key: " << exc.what());
+ }
+ if (key->algo_name().compare("RSA") != 0) {
+ delete key;
+ isc_throw(BadKey, "not a RSA Public Key");
+ }
+ pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+ if (!pub_) {
+ delete key;
+ isc_throw(LibraryError, "dynamic_cast");
+ }
+ } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+ // bind9 .key file (RDATA)
+ // warn when password not empty
+ if ((hash_ != MD5) && (hash_ != SHA1) &&
+ (hash_ != SHA256) && (hash_ != SHA512)) {
+ isc_throw(UnsupportedAlgorithm,
+ "Not compatible hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ std::ifstream fp(filename.c_str(), std::ios::in);
+ if (!fp.is_open()) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ std::string line;
+ bool found = false;
+ while (fp.good()) {
+ getline(fp, line);
+ if (line.empty() || (line[0] == ';')) {
+ continue;
+ }
+ if (line.find("DNSKEY") == std::string::npos) {
+ continue;
+ }
+ found = true;
+ if (line[line.size() - 1] == '\n') {
+ line.erase(line.size() - 1);
+ }
+ break;
+ }
+ fp.close();
+ if (!found) {
+ isc_throw(BadKey, "Can't find a DNSKEY");
+ }
+ const std::string b64 =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
+ "ghijklmnopqrstuvwxyz0123456789+/=";
+ const std::string value = line.substr(line.find_last_not_of(b64));
+ std::vector<uint8_t> bin;
+ try {
+ util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ isc_throw(BadKey, "Can't decode base64: " << exc.what());
+ }
+ size_t key_len = bin.size();
+ const uint8_t* p = &bin[0];
+ size_t e_bytes = *p++;
+ --key_len;
+ if (e_bytes == 0) {
+ if (key_len < 2) {
+ isc_throw(BadKey,
+ "Bad RSA Public Key: short exponent length");
+ }
+ e_bytes = (*p++) << 8;
+ e_bytes += *p++;
+ key_len -= 2;
+ }
+ if (key_len < e_bytes) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+ }
+ if ((key_len - e_bytes) < 64) {
+ isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+ (key_len - e_bytes) * 8);
+ }
+ if ((key_len - e_bytes) > 512) {
+ isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+ (key_len - e_bytes) * 8);
+ }
+ Botan::BigInt e(p, e_bytes);
+ p += e_bytes;
+ key_len -= e_bytes;
+ Botan::BigInt n(p, key_len);
+ try {
+ pub_.reset(new Botan::RSA_PublicKey(n, e));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "RSA_PublicKey: " << exc.what());
+ }
+ } else if (kind_ == PUBLIC) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Public Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == CERT) && (key_format == ASN1)) {
+ // X.509 Public Key Certificate PEM file
+ // warn when password not empty
+ try {
+ x509_.reset(new Botan::X509_Certificate(filename));
+ } catch (const std::exception& exc) {
+ isc_throw(BadKey, "X509_Certificate: " << exc.what());
+ }
+ const Botan::AlgorithmIdentifier
+ sig_algo(x509_->signature_algorithm());
+ if (hash_ == MD5) {
+ const Botan::AlgorithmIdentifier
+ rsa_md5("1.2.840.113549.1.1.4",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_md5) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA MD5 certificate");
+ }
+ } else if (hash_ == SHA1) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha1("1.2.840.113549.1.1.5",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha1) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA1 certificate");
+ }
+ } else if (hash_ == SHA224) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha224("1.2.840.113549.1.1.14",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha224) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA224 certificate");
+ }
+ } else if (hash_ == SHA256) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha256("1.2.840.113549.1.1.11",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha256) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA256 certificate");
+ }
+ } else if (hash_ == SHA384) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha384("1.2.840.113549.1.1.12",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha384) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA384 certificate");
+ }
+ } else if (hash_ == SHA512) {
+ const Botan::AlgorithmIdentifier
+ rsa_sha512("1.2.840.113549.1.1.13",
+ Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ if (sig_algo != rsa_sha512) {
+ x509_.reset();
+ isc_throw(BadKey, "Require a RSA SHA512 certificate");
+ }
+ } else {
+ x509_.reset();
+ isc_throw(UnsupportedAlgorithm,
+ "Bad hash algorithm for certificate: " <<
+ static_cast<int>(hash_));
+ }
+ Botan::Public_Key* key;
+ try {
+ key = x509_->subject_public_key();
+ } catch (const std::exception& exc) {
+ x509_.reset();
+ isc_throw(BadKey, "subject_public_key: " << exc.what());
+ }
+ if (key->algo_name().compare("RSA") != 0) {
+ delete key;
+ x509_.reset();
+ isc_throw(BadKey, "not a RSA Public Key");
+ }
+ pub_.reset(dynamic_cast<Botan::RSA_PublicKey*>(key));
+ if (!pub_) {
+ delete key;
+ x509_.reset();
+ isc_throw(LibraryError, "dynamic_cast");
+ }
+ } else if (kind_ == CERT) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown Public Key Certificate format: " <<
+ static_cast<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " << static_cast<int>(kind_));
+ }
+
+ if (kind_ == PRIVATE) {
+ try {
+ 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<int>(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<const Botan::byte*>(data), len);
+ } else {
+ verifier_->update(reinterpret_cast<const Botan::byte*>(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<Botan::byte> 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<Botan::byte> 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<uint8_t> RsaAsymImpl::sign(size_t len, const AsymFormat) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result;
+ Botan::AutoSeeded_RNG rng;
+ b_result = signer_->signature(rng);
+ if (len > b_result.size()) {
+ return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
+ } else {
+ return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
+ }
+ } catch (const std::exception& exc) {
+ isc_throw(LibraryError, "signature: " << exc.what());
+ }
+}
+
+/// @brief Verify an existing signature
+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<const Botan::byte*>(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<int>(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<uint8_t>
+ 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<Botan::byte> der;
+ try {
+ der = priv_->pkcs8_private_key();
+ } catch (const std::exception& exc) {
+ isc_throw(LibraryError, "pkcs8_private_key: " << exc.what());
+ }
+ return std::vector<uint8_t>(der.begin(), der.end());
+ } else if (key_kind == PRIVATE) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Private Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
+ // PKCS#1 Public Key
+ Botan::MemoryVector<Botan::byte> der;
+ try {
+ der = pub_->x509_subject_public_key();
+ } catch (const std::exception& exc) {
+ isc_throw(LibraryError,
+ "x509_subject_public_key: " << exc.what());
+ }
+ return std::vector<uint8_t>(der.begin(), der.end());
+ } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo
+ Botan::MemoryVector<Botan::byte> ber;
+ try {
+ ber = Botan::X509::BER_encode(*pub_);
+ } catch (const std::exception& exc) {
+ isc_throw(LibraryError, "X509::BER_encode: " << exc.what());
+ }
+ return std::vector<uint8_t>(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<uint8_t> rdata(x_bytes + e_bytes + mod_bytes);
+ if (e_bytes < 256) {
+ rdata[0] = e_bytes;
+ } else {
+ rdata[0] = 0;
+ rdata[1] = (e_bytes >> 8) & 0xff;
+ rdata[2] = e_bytes & 0xff;
+ }
+ pub_->get_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<int>(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<Botan::byte> ber;
+ try {
+ ber = x509_->BER_encode();
+ } catch (const std::exception& exc) {
+ isc_throw(LibraryError, "BER_encode" << exc.what());
+ }
+ return std::vector<uint8_t>(ber.begin(), ber.end());
+ } else if (key_kind == CERT) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown Certificate format: " <<
+ static_cast<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " << static_cast<int>(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<int>(hash_));
+ }
+ std::ofstream fp(filename.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ if (!fp.is_open()) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ fp << "Private-key-format: v1.2\n";
+ if (hash_ == MD5) {
+ fp << "Algorithm: 1 (RSA)\n";
+ } else if (hash_ == SHA1) {
+ fp << "Algorithm: 5 (RSASHA1)\n";
+ } else if (hash_ == SHA256) {
+ fp << "Algorithm: 8 (RSASHA256)\n";
+ } else if (hash_ == SHA512) {
+ fp << "Algorithm: 10 (RSASHA512)\n";
+ }
+ std::vector<uint8_t> bin;
+ bin.resize(priv_->get_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<int>(key_format));
+ } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo PEM file
+ // warn when password not empty
+ std::string pem;
+ try {
+ pem = Botan::X509::PEM_encode(*pub_);
+ } catch (const std::exception& exc) {
+ isc_throw(LibraryError, "X509::PEM_encode: " << exc.what());
+ }
+ std::ofstream fp(filename.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ if (fp.is_open()) {
+ fp.write(pem.c_str(), pem.size());
+ fp.close();
+ } else {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ } else if ((key_kind == PUBLIC) && (key_format == DNS)) {
+ // bind9 .key file (RDATA)
+ // warn when password not empty
+ std::vector<uint8_t> bin = exportkey(key_kind, key_format);
+ std::ofstream fp(filename.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ if (!fp.is_open()) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ fp << "; DNSKEY RDATA: " << util::encode::encodeBase64(bin) << '\n';
+ fp.close();
+ } else if (key_kind == PUBLIC) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Public Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((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<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " << static_cast<int>(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
--- /dev/null
+// 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<uint8_t> 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<uint8_t> 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<Botan::RSA_PrivateKey> priv_;
+ /// @brief The protected pointer to the Botan public key
+ boost::scoped_ptr<Botan::RSA_PublicKey> pub_;
+ /// @brief The protected pointer to the Botan certificate
+ boost::scoped_ptr<Botan::X509_Certificate> x509_;
+ /// @brief The protected pointer to the Botan signer
+ boost::scoped_ptr<Botan::PK_Signer> signer_;
+ /// @brief The protected pointer to the Botan verifier
+ boost::scoped_ptr<Botan::PK_Verifier> verifier_;
+};
+
+} // namespace cryptolink
+} // namespace isc
#include <cryptolink.h>
#include <cryptolink/crypto_asym.h>
-#include <boost/scoped_ptr.hpp>
-
#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/rsa.h>
-#include <openssl/x509.h>
-#include <util/encode/base64.h>
#include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h>
#include <cryptolink/openssl_common.h>
-
-#include <cstdio>
-#include <cstring>
+#include <cryptolink/openssl_rsa.h>
namespace {
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<int>(hash_));
- }
- if (key_len == 0) {
- isc_throw(BadKey, "Bad RSA " <<
- (kind_ != CERT ? "key" : "cert") <<
- " length: 0");
- }
-
- if ((kind_ == PRIVATE) && (key_format == BASIC)) {
- // PKCS#1 Private Key
- const unsigned char* p =
- reinterpret_cast<const unsigned char*>(key);
- RSA* rsa = d2i_RSAPrivateKey(NULL, &p,
- static_cast<long>(key_len));
- if (!rsa) {
- isc_throw(BadKey, "d2i_RSAPrivateKey");
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if (kind_ == PRIVATE) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Private Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
- // PKCS#1 Public Key
- const unsigned char* p =
- reinterpret_cast<const unsigned char*>(key);
- RSA* rsa = d2i_RSAPublicKey(NULL, &p,
- static_cast<long>(key_len));
- if (!rsa) {
- isc_throw(BadKey, "d2i_RSAPublicKey");
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo
- const unsigned char* p =
- reinterpret_cast<const unsigned char*>(key);
- RSA* rsa = d2i_RSA_PUBKEY(NULL, &p, static_cast<long>(key_len));
- if (!rsa) {
- isc_throw(BadKey, "d2i_RSA_PUBKEY");
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
- // RFC 3110 DNS wire format
- // key_len == 0 was already checked
- const uint8_t* p =
- reinterpret_cast<const uint8_t*>(key);
- unsigned int e_bytes = *p++;
- --key_len;
- if (e_bytes == 0) {
- if (key_len < 2) {
- isc_throw(BadKey,
- "Bad RSA Public Key: short exponent length");
- }
- e_bytes = (*p++) << 8;
- e_bytes += *p++;
- key_len -= 2;
- }
- if (key_len < e_bytes) {
- isc_throw(BadKey, "Bad RSA Public Key: short exponent");
- }
- if ((key_len - e_bytes) < 64) {
- isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
- (key_len - e_bytes) * 8);
- }
- if ((key_len - e_bytes) > 512) {
- isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
- (key_len - e_bytes) * 8);
- }
- RSA* rsa = RSA_new();
- if (!rsa) {
- throw std::bad_alloc();
- }
- rsa->e = BN_bin2bn(p, e_bytes, NULL);
- p += e_bytes;
- key_len -= e_bytes;
- rsa->n = BN_bin2bn(p, key_len, NULL);
- if (!rsa->e || !rsa->n) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if (kind_ == PUBLIC) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Public Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == CERT) && (key_format == ASN1)) {
- // X.509 Public Key Certificate
- const unsigned char* p =
- reinterpret_cast<const unsigned char*>(key);
- x509_ = d2i_X509(NULL, &p, static_cast<long>(key_len));
- if (!x509_) {
- isc_throw(BadKey, "d2i_X509");
- }
- int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
- if (hash_ == MD5) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.4")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA MD5 certificate");
- }
- } else if (hash_ == SHA1) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.5")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA1 certificate");
- }
- } else if (hash_ == SHA224) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.14")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA224 certificate");
- }
- } else if (hash_ == SHA256) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.11")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA256 certificate");
- }
- } else if (hash_ == SHA384) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.12")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA384 certificate");
- }
- } else if (hash_ == SHA512) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.13")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA512 certificate");
- }
- } else {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(UnsupportedAlgorithm,
- "Bad hash algorithm for certificate: " <<
- static_cast<int>(hash_));
- }
- pkey_ = X509_get_pubkey(x509_);
- if (!pkey_) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "X509_get_pubkey");
- }
- if (pkey_->type != EVP_PKEY_RSA) {
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "not a RSA Public Key");
- }
- } else if (kind_ == CERT) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown Certificate format: " <<
- static_cast<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(kind_));
- }
-
- mdctx_.reset(new EVP_MD_CTX);
- EVP_MD_CTX_init(mdctx_.get());
-
- if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
- EVP_MD_CTX_cleanup(mdctx_.get());
- EVP_PKEY_free(pkey_);
- pkey_ =NULL;
- isc_throw(LibraryError, "EVP_DigestInit_ex");
- }
- }
-
- /// @brief Constructor from a key file with password,
- /// asym and hash algorithm, key kind and key binary format
- ///
- /// See constructor of the @ref isc::cryptolink::Asym class for details.
- ///
- /// @param filename The key file name/path
- /// @param password The PKCS#8 password
- /// @param 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<int>(hash_));
- }
-
- if ((kind_ == PRIVATE) &&
- ((key_format == BASIC) || (key_format == ASN1))) {
- // PKCS#1 or PKCS#8 Private Key PEM file
- FILE* fp = fopen(filename.c_str(), "r");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- char* pwd = NULL;
- if (!password.empty()) {
- pwd = const_cast<char*>(password.c_str());
- }
- pkey_ = PEM_read_PrivateKey(fp, NULL, 0, pwd);
- fclose(fp);
- if (!pkey_) {
- isc_throw(BadKey, "PEM_read_PrivateKey");
- }
- if (pkey_->type != EVP_PKEY_RSA) {
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(BadKey, "not a RSA Private Key");
- }
- } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
- // bind9 .private file
- // warn when password not empty
- if ((hash_ != MD5) && (hash_ != SHA1) &&
- (hash_ != SHA256) && (hash_ != SHA512)) {
- isc_throw(UnsupportedAlgorithm,
- "Not compatible hash algorithm: " <<
- static_cast<int>(hash_));
- }
- FILE* fp = fopen(filename.c_str(), "r");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- bool got_algorithm = false;
- bool got_modulus = false;
- bool got_pub_exponent = false;
- bool got_priv_exponent = false;
- bool got_prime1 = false;
- bool got_prime2 = false;
- bool got_exponent1 = false;
- bool got_exponent2 = false;
- bool got_coefficient = false;
- RSA* rsa = RSA_new();
- char line[4096];
- while (fgets(line, sizeof(line), fp)) {
- if (strncmp(line, "Algorithm:", strlen("Algorithm:")) == 0) {
- if (got_algorithm) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Algorithm entries");
- }
- got_algorithm = true;
- std::string value(line + strlen("Algorithm:") + 1);
- int alg = std::atoi(value.c_str());
- if (alg == 1) {
- // RSAMD5
- if (hash_ != MD5) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Require a RSA MD5 key");
- }
- } else if ((alg == 5) || (alg == 7)) {
- // RSASHA1 or RSASHA1-NSEC3-SHA1
- if (hash_ != SHA1) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Require a RSA SHA1 key");
- }
- } else if (alg == 8) {
- // RSASHA256
- if (hash_ != SHA256) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Require a RSA SHA256 key");
- }
- } else if (alg == 10) {
- // RSASHA512
- if (hash_ != SHA512) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Require a RSA SHA512 key");
- }
- } else {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Bad Algorithm: " << alg);
- }
- } else if (strncmp(line, "Modulus:",
- strlen("Modulus:")) == 0) {
- if (got_modulus) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Modulus entries");
- }
- got_modulus = true;
- std::string value(line + strlen("Modulus:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Modulus: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->n = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "PublicExponent:",
- strlen("PublicExponent:")) == 0) {
- if (got_pub_exponent) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two PublicExponent entries");
- }
- got_pub_exponent = true;
- std::string value(line + strlen("PublicExponent:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "PublicExponent: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->e = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "PrivateExponent:",
- strlen("PrivateExponent:")) == 0) {
- if (got_priv_exponent) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two PrivateExponent entries");
- }
- got_priv_exponent = true;
- std::string value(line + strlen("PrivateExponent:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "PrivateExponent: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->d = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "Prime1:", strlen("Prime1:")) == 0) {
- if (got_prime1) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Prime1 entries");
- }
- got_prime1 = true;
- std::string value(line + strlen("Prime1:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Prime1: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->p = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "Prime2:", strlen("Prime2:")) == 0) {
- if (got_prime2) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Prime2 entries");
- }
- got_prime2 = true;
- std::string value(line + strlen("Prime2:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Prime2: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->q = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "Exponent1:",
- strlen("Exponent1:")) == 0) {
- if (got_exponent1) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Exponent1 entries");
- }
- got_exponent1 = true;
- std::string value(line + strlen("Exponent1:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Exponent1: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->dmp1 = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "Exponent2:",
- strlen("Exponent2:")) == 0) {
- if (got_exponent2) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Exponent2 entries");
- }
- got_exponent2 = true;
- std::string value(line + strlen("Exponent2:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Exponent2: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->dmq1 = BN_bin2bn(&bin[0], len, NULL);
- } else if (strncmp(line, "Coefficient:",
- strlen("Coefficient:")) == 0) {
- if (got_coefficient) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Two Coefficient entries");
- }
- got_coefficient = true;
- std::string value(line + strlen("Coefficient:") + 1);
- std::vector<uint8_t> bin;
- try {
- isc::util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- RSA_free(rsa);
- fclose(fp);
- isc_throw(BadKey, "Coefficient: " << exc.what());
- }
- int len = static_cast<int>(bin.size());
- rsa->iqmp = BN_bin2bn(&bin[0], len, NULL);
- }
- }
- fclose(fp);
- if (!got_algorithm) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Algorithm entry");
- }
- if (!got_modulus) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Modulus entry");
- }
- if (!got_pub_exponent) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing PublicExponent entry");
- }
- if (!got_priv_exponent) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing PrivateExponent entry");
- }
- if (!got_prime1) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Prime1 entry");
- }
- if (!got_prime2) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Prime2 entry");
- }
- if (!got_exponent1) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Exponent1 entry");
- }
- if (!got_exponent2) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Exponent2 entry");
- }
- if (!got_coefficient) {
- RSA_free(rsa);
- isc_throw(BadKey, "Missing Coefficient entry");
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
- // PKCS#1 PEM file
- // warn when password not empty
- FILE* fp = fopen(filename.c_str(), "r");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- RSA* rsa = PEM_read_RSAPublicKey(fp, NULL, 0, NULL);
- fclose(fp);
- if (!rsa) {
- isc_throw(BadKey, "PEM_read_RSAPublicKey");
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo PEM file
- // warn when password not empty
- FILE* fp = fopen(filename.c_str(), "r");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- char* pwd = NULL;
- if (!password.empty()) {
- pwd = const_cast<char*>(password.c_str());
- }
- pkey_ = PEM_read_PUBKEY(fp, NULL, 0, pwd);
- fclose(fp);
- if (!pkey_) {
- isc_throw(BadKey, "PEM_read_PUBKEY");
- }
- if (pkey_->type != EVP_PKEY_RSA) {
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(BadKey, "not a RSA Public Key");
- }
- } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
- // bind9 .key file (RDATA)
- // warn when password not empty
- if ((hash_ != MD5) && (hash_ != SHA1) &&
- (hash_ != SHA256) && (hash_ != SHA512)) {
- isc_throw(UnsupportedAlgorithm,
- "Not compatible hash algorithm: " <<
- static_cast<int>(hash_));
- }
- FILE* fp = fopen(filename.c_str(), "r");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- char line[4096];
- bool found = false;
- while (fgets(line, sizeof(line), fp)) {
- if ((line[0] == '\0') || (line[0] == ';')) {
- continue;
- }
- if (strstr(line, "DNSKEY") == NULL) {
- continue;
- }
- found = true;
- if (line[strlen(line) - 1] == '\n') {
- line[strlen(line) - 1] = 0;
- }
- break;
- }
- fclose(fp);
- if (!found) {
- isc_throw(BadKey, "Can't find a DNSKEY");
- }
- const char b64[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
- "ghijklmnopqrstuvwxyz0123456789+/=";
- size_t last = strlen(line) - 1;
- while (strchr(b64, static_cast<int>(line[last])) != NULL) {
- --last;
- }
- const std::string value(line + last + 1);
- std::vector<uint8_t> bin;
- try {
- util::encode::decodeBase64(value, bin);
- } catch (const BadValue& exc) {
- isc_throw(BadKey, "Can't decode base64: " << exc.what());
- }
- size_t key_len = bin.size();
- const uint8_t* p = &bin[0];
- unsigned int e_bytes = *p++;
- --key_len;
- if (e_bytes == 0) {
- if (key_len < 2) {
- isc_throw(BadKey,
- "Bad RSA Public Key: short exponent length");
- }
- e_bytes = (*p++) << 8;
- e_bytes += *p++;
- key_len -= 2;
- }
- if (key_len < e_bytes) {
- isc_throw(BadKey, "Bad RSA Public Key: short exponent");
- }
- if ((key_len - e_bytes) < 64) {
- isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
- (key_len - e_bytes) * 8);
- }
- if ((key_len - e_bytes) > 512) {
- isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
- (key_len - e_bytes) * 8);
- }
- RSA* rsa = RSA_new();
- if (!rsa) {
- throw std::bad_alloc();
- }
- rsa->e = BN_bin2bn(p, e_bytes, NULL);
- p += e_bytes;
- key_len -= e_bytes;
- rsa->n = BN_bin2bn(p, key_len, NULL);
- if (!rsa->e || !rsa->n) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- pkey_ = EVP_PKEY_new();
- if (!pkey_) {
- RSA_free(rsa);
- throw std::bad_alloc();
- }
- if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
- RSA_free(rsa);
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
- }
- // set1 bumped the reference counter
- RSA_free(rsa);
- } else if (kind_ == PUBLIC) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Public Key format: " <<
- static_cast<int>(key_format));
- } else if ((kind_ == CERT) && (key_format == ASN1)) {
- // Public Key Certificate PEM file
- // warn when password not empty
- FILE* fp = fopen(filename.c_str(), "r");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- x509_ = PEM_read_X509(fp, NULL, 0, NULL);
- fclose(fp);
- if (!x509_) {
- isc_throw(BadKey, "PEM_read_X509");
- }
- int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
- if (hash_ == MD5) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.4")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA MD5 certificate");
- }
- } else if (hash_ == SHA1) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.5")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA1 certificate");
- }
- } else if (hash_ == SHA224) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.14")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA224 certificate");
- }
- } else if (hash_ == SHA256) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.11")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA256 certificate");
- }
- } else if (hash_ == SHA384) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.12")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA384 certificate");
- }
- } else if (hash_ == SHA512) {
- if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.13")) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "Require a RSA SHA512 certificate");
- }
- } else {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(UnsupportedAlgorithm,
- "Bad hash algorithm for certificate: " <<
- static_cast<int>(hash_));
- }
- pkey_ = X509_get_pubkey(x509_);
- if (!pkey_) {
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "X509_get_pubkey");
- }
- if (pkey_->type != EVP_PKEY_RSA) {
- EVP_PKEY_free(pkey_);
- pkey_ = NULL;
- X509_free(x509_);
- x509_ = NULL;
- isc_throw(BadKey, "not a RSA Public Key");
- }
- } else if (kind_ == CERT) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown Public Key Certificate format: " <<
- static_cast<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(kind_));
- }
- mdctx_.reset(new EVP_MD_CTX);
- EVP_MD_CTX_init(mdctx_.get());
-
- if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
- EVP_MD_CTX_cleanup(mdctx_.get());
- EVP_PKEY_free(pkey_);
- pkey_ =NULL;
- isc_throw(LibraryError, "EVP_DigestInit_ex");
- }
- }
-
- /// @brief Destructor
- 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<size_t>(EVP_PKEY_bits(pkey_)));
- }
-
- /// @brief Returns the output size of the signature
- ///
- /// \param sig_format The signature binary format
- size_t getSignatureLength(const AsymFormat sig_format) const {
- switch (sig_format) {
- case BASIC:
- case ASN1:
- case DNS:
- // In all cases a big integer of the size of n
- return (static_cast<size_t>(EVP_PKEY_size(pkey_)));
- default:
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Signature format: " <<
- static_cast<int>(sig_format));
- }
- }
-
- /// @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<unsigned char> sig(size);
- if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
- isc_throw(LibraryError, "EVP_SignFinal");
- }
- if (len > size) {
- len = size;
- }
- result.writeData(&sig[0], len);
- }
-
- /// @brief Calculate the final signature
- ///
- /// See @ref isc::cryptolink::AsymBase::sign() for details.
- void sign(void* result, size_t len, const AsymFormat sig_format) {
- unsigned int size = getSignatureLength(sig_format);
- ossl::SecBuf<unsigned char> sig(size);
- if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
- isc_throw(LibraryError, "EVP_SignFinal");
- }
- if (len > size) {
- len = size;
- }
- std::memcpy(result, &sig[0], len);
- }
-
- /// @brief Calculate the final signature
- ///
- /// See @ref isc::cryptolink::AsymBase::sign() for details.
- std::vector<uint8_t> sign(size_t len, const AsymFormat sig_format) {
- unsigned int size = getSignatureLength(sig_format);
- ossl::SecBuf<unsigned char> sig(size);
- if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
- isc_throw(LibraryError, "EVP_SignFinal");
- }
- // resize to min(len, size)
- sig.resize(len < size ? len : size);
- return (std::vector<uint8_t>(sig.begin(), sig.end()));
- }
-
- /// @brief Verify an existing signature
- ///
- /// See @ref isc::cryptolink::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<unsigned char*>(const_cast<void*>(sig));
- unsigned int siglen = static_cast<unsigned int>(len);
- int status = EVP_VerifyFinal(mdctx_.get(), sigbuf, siglen, pkey_);
- switch (status) {
- case 1:
- return true;
- case 0:
- return false;
- case -1:
- default:
- isc_throw(LibraryError, "EVP_VerifyFinal");
- }
- }
-
- /// @brief 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<int>(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<uint8_t> exportkey(const AsymKeyKind key_kind,
- const AsymFormat key_format) const {
- if ((key_kind == PRIVATE) && (key_format == BASIC)) {
- // PKCS#1 Private Key
- if (kind_ != PRIVATE) {
- isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
- }
- RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
- if (!rsa) {
- isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
- }
- int len = i2d_RSAPrivateKey(rsa, NULL);
- if (len < 0) {
- RSA_free(rsa);
- isc_throw(LibraryError, "i2d_RSAPrivateKey 0");
- }
- std::vector<uint8_t> der(static_cast<size_t>(len));
- unsigned char* p = &der[0];
- len = i2d_RSAPrivateKey(rsa, &p);
- RSA_free(rsa);
- if (len != static_cast<int>(der.size())) {
- isc_throw(LibraryError, "i2d_RSAPrivateKey");
- }
- return der;
- } else if (key_kind == PRIVATE) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Private Key format: " <<
- static_cast<int>(key_format));
- } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
- // PKCS#1 Public Key
- RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
- if (!rsa) {
- isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
- }
- int len = i2d_RSAPublicKey(rsa, NULL);
- if (len < 0) {
- RSA_free(rsa);
- isc_throw(LibraryError, "i2d_RSAPublicKey 0");
- }
- std::vector<uint8_t> der(static_cast<size_t>(len));
- unsigned char* p = &der[0];
- len = i2d_RSAPublicKey(rsa, &p);
- RSA_free(rsa);
- if (len != static_cast<int>(der.size())) {
- isc_throw(LibraryError, "i2d_RSAPublicKey");
- }
- return der;
- } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
- // SubjectPublicKeyInfo
- RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
- if (!rsa) {
- isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
- }
- int len = i2d_RSA_PUBKEY(rsa, NULL);
- if (len < 0) {
- RSA_free(rsa);
- isc_throw(LibraryError, "i2d_RSA_PUBKEY 0");
- }
- std::vector<uint8_t> der(static_cast<size_t>(len));
- unsigned char* p = &der[0];
- len = i2d_RSA_PUBKEY(rsa, &p);
- RSA_free(rsa);
- if (len != static_cast<int>(der.size())) {
- isc_throw(LibraryError, "i2d_RSA_PUBKEY");
- }
- return der;
- } else if ((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<uint8_t> rdata(x_bytes + e_bytes + mod_bytes);
- if (e_bytes < 256) {
- rdata[0] = e_bytes;
- } else {
- rdata[0] = 0;
- rdata[1] = (e_bytes >> 8) & 0xff;
- rdata[2] = e_bytes & 0xff;
- }
- BN_bn2bin(rsa->e, &rdata[x_bytes]);
- BN_bn2bin(rsa->n, &rdata[x_bytes + e_bytes]);
- RSA_free(rsa);
- return rdata;
- } else if (key_kind == PUBLIC) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Public Key format: " <<
- static_cast<int>(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<uint8_t> ber(static_cast<size_t>(len));
- unsigned char* p = &ber[0];
- len = i2d_X509(x509_, &p);
- if (len != static_cast<int>(ber.size())) {
- isc_throw(LibraryError, "i2d_X509");
- }
- return ber;
- } else if (key_kind == CERT) {
- isc_throw(UnsupportedAlgorithm,
- "Unknown Certificate format: " <<
- static_cast<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(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<char*>(password.c_str());
- enc = EVP_des_ede3_cbc();
- if (!enc) {
- isc_throw(LibraryError, "EVP_des_ede3_cbc");
- }
- }
- FILE* fp = fopen(filename.c_str(), "w");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- if (!PEM_write_PKCS8PrivateKey(fp, pkey_, enc, pwd,
- static_cast<int>(password.size()),
- 0, NULL)) {
- fclose(fp);
- isc_throw(LibraryError, "PEM_write_PKCS8PrivateKey");
- }
- fclose(fp);
- } else if ((key_kind == PRIVATE) && (key_format == DNS)) {
- // bind9 .private file
- if (kind_ != PRIVATE) {
- isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
- }
- if ((hash_ != MD5) && (hash_ != SHA1) &&
- (hash_ != SHA256) && (hash_ != SHA512)) {
- isc_throw(UnsupportedAlgorithm,
- "Not compatible hash algorithm: " <<
- static_cast<int>(hash_));
- }
- FILE* fp = fopen(filename.c_str(), "w");
- if (!fp) {
- isc_throw(BadKey, "Can't open file: " << filename);
- }
- RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
- if (!rsa) {
- fclose(fp);
- isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
- }
- fprintf(fp, "Private-key-format: v1.2\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<uint8_t> 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<int>(key_format));
- } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
- // PKCS#1 PEM file
- // warn when password not empty
- FILE* fp = fopen(filename.c_str(), "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<uint8_t> 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<int>(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<int>(key_format));
- } else {
- isc_throw(UnsupportedAlgorithm,
- "Unknown RSA Key kind: " <<
- static_cast<int>(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<EVP_MD_CTX> mdctx_;
- /// @brief The raw pointer to the OpenSSL EVP_PKEY structure
- /// There is no EVP_PKEY_init() or EVP_PKEY_cleanup() so
- /// a smart pointer cannot be used.
- EVP_PKEY* pkey_;
- /// @brief The raw pointer to the OpenSSL X509 structure
- /// There is no X509_init() or X509_cleanup() so
- /// a smart pointer cannot be used.
- X509* x509_;
-};
-
Asym::Asym(const void* key, size_t key_len,
const AsymAlgorithm asym_algorithm,
const HashAlgorithm hash_algorithm,
--- /dev/null
+// 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 <cryptolink.h>
+#include <cryptolink/crypto_asym.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include <util/encode/base64.h>
+#include <cryptolink/openssl_common.h>
+#include <cryptolink/openssl_rsa.h>
+
+#include <cstdio>
+#include <cstring>
+
+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<int>(hash_));
+ }
+ if (key_len == 0) {
+ isc_throw(BadKey, "Bad RSA " <<
+ (kind_ != CERT ? "key" : "cert") << " length: 0");
+ }
+
+ if ((kind_ == PRIVATE) && (key_format == BASIC)) {
+ // PKCS#1 Private Key
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+ RSA* rsa = d2i_RSAPrivateKey(NULL, &p, static_cast<long>(key_len));
+ if (!rsa) {
+ isc_throw(BadKey, "d2i_RSAPrivateKey");
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if (kind_ == PRIVATE) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Private Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
+ // PKCS#1 Public Key
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+ RSA* rsa = d2i_RSAPublicKey(NULL, &p, static_cast<long>(key_len));
+ if (!rsa) {
+ isc_throw(BadKey, "d2i_RSAPublicKey");
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+ RSA* rsa = d2i_RSA_PUBKEY(NULL, &p, static_cast<long>(key_len));
+ if (!rsa) {
+ isc_throw(BadKey, "d2i_RSA_PUBKEY");
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+ // RFC 3110 DNS wire format
+ // key_len == 0 was already checked
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(key);
+ unsigned int e_bytes = *p++;
+ --key_len;
+ if (e_bytes == 0) {
+ if (key_len < 2) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent length");
+ }
+ e_bytes = (*p++) << 8;
+ e_bytes += *p++;
+ key_len -= 2;
+ }
+ if (key_len < e_bytes) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+ }
+ if ((key_len - e_bytes) < 64) {
+ isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+ (key_len - e_bytes) * 8);
+ }
+ if ((key_len - e_bytes) > 512) {
+ isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+ (key_len - e_bytes) * 8);
+ }
+ RSA* rsa = RSA_new();
+ if (!rsa) {
+ throw std::bad_alloc();
+ }
+ rsa->e = BN_bin2bn(p, e_bytes, NULL);
+ p += e_bytes;
+ key_len -= e_bytes;
+ rsa->n = BN_bin2bn(p, key_len, NULL);
+ if (!rsa->e || !rsa->n) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if (kind_ == PUBLIC) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Public Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == CERT) && (key_format == ASN1)) {
+ // X.509 Public Key Certificate
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
+ x509_ = d2i_X509(NULL, &p, static_cast<long>(key_len));
+ if (!x509_) {
+ isc_throw(BadKey, "d2i_X509");
+ }
+ int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+ if (hash_ == MD5) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.4")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA MD5 certificate");
+ }
+ } else if (hash_ == SHA1) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.5")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA1 certificate");
+ }
+ } else if (hash_ == SHA224) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.14")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA224 certificate");
+ }
+ } else if (hash_ == SHA256) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.11")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA256 certificate");
+ }
+ } else if (hash_ == SHA384) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.12")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA384 certificate");
+ }
+ } else if (hash_ == SHA512) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.13")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA512 certificate");
+ }
+ } else {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(UnsupportedAlgorithm,
+ "Bad hash algorithm for certificate: " <<
+ static_cast<int>(hash_));
+ }
+ pkey_ = X509_get_pubkey(x509_);
+ if (!pkey_) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "X509_get_pubkey");
+ }
+ if (pkey_->type != EVP_PKEY_RSA) {
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "not a RSA Public Key");
+ }
+ } else if (kind_ == CERT) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown Certificate format: " <<
+ static_cast<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " << static_cast<int>(kind_));
+ }
+
+ mdctx_.reset(new EVP_MD_CTX);
+ EVP_MD_CTX_init(mdctx_.get());
+
+ if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
+ EVP_MD_CTX_cleanup(mdctx_.get());
+ EVP_PKEY_free(pkey_);
+ pkey_ =NULL;
+ isc_throw(LibraryError, "EVP_DigestInit_ex");
+ }
+}
+
+/// @brief Constructor from a key file with password,
+/// asym and hash algorithm, key kind and key binary format
+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<int>(hash_));
+ }
+
+ if ((kind_ == PRIVATE) &&
+ ((key_format == BASIC) || (key_format == ASN1))) {
+ // PKCS#1 or PKCS#8 Private Key PEM file
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ char* pwd = NULL;
+ if (!password.empty()) {
+ pwd = const_cast<char*>(password.c_str());
+ }
+ pkey_ = PEM_read_PrivateKey(fp, NULL, 0, pwd);
+ fclose(fp);
+ if (!pkey_) {
+ isc_throw(BadKey, "PEM_read_PrivateKey");
+ }
+ if (pkey_->type != EVP_PKEY_RSA) {
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(BadKey, "not a RSA Private Key");
+ }
+ } else if ((kind_ == PRIVATE) && (key_format == DNS)) {
+ // bind9 .private file
+ // warn when password not empty
+ if ((hash_ != MD5) && (hash_ != SHA1) &&
+ (hash_ != SHA256) && (hash_ != SHA512)) {
+ isc_throw(UnsupportedAlgorithm,
+ "Not compatible hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ bool got_algorithm = false;
+ bool got_modulus = false;
+ bool got_pub_exponent = false;
+ bool got_priv_exponent = false;
+ bool got_prime1 = false;
+ bool got_prime2 = false;
+ bool got_exponent1 = false;
+ bool got_exponent2 = false;
+ bool got_coefficient = false;
+ RSA* rsa = RSA_new();
+ char line[4096];
+ while (fgets(line, sizeof(line), fp)) {
+ if (strncmp(line, "Algorithm:", strlen("Algorithm:")) == 0) {
+ if (got_algorithm) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Algorithm entries");
+ }
+ got_algorithm = true;
+ std::string value(line + strlen("Algorithm:") + 1);
+ int alg = std::atoi(value.c_str());
+ if (alg == 1) {
+ // RSAMD5
+ if (hash_ != MD5) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Require a RSA MD5 key");
+ }
+ } else if ((alg == 5) || (alg == 7)) {
+ // RSASHA1 or RSASHA1-NSEC3-SHA1
+ if (hash_ != SHA1) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Require a RSA SHA1 key");
+ }
+ } else if (alg == 8) {
+ // RSASHA256
+ if (hash_ != SHA256) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Require a RSA SHA256 key");
+ }
+ } else if (alg == 10) {
+ // RSASHA512
+ if (hash_ != SHA512) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Require a RSA SHA512 key");
+ }
+ } else {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Bad Algorithm: " << alg);
+ }
+ } else if (strncmp(line, "Modulus:", strlen("Modulus:")) == 0) {
+ if (got_modulus) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Modulus entries");
+ }
+ got_modulus = true;
+ std::string value(line + strlen("Modulus:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Modulus: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->n = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "PublicExponent:",
+ strlen("PublicExponent:")) == 0) {
+ if (got_pub_exponent) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two PublicExponent entries");
+ }
+ got_pub_exponent = true;
+ std::string value(line + strlen("PublicExponent:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "PublicExponent: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->e = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "PrivateExponent:",
+ strlen("PrivateExponent:")) == 0) {
+ if (got_priv_exponent) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two PrivateExponent entries");
+ }
+ got_priv_exponent = true;
+ std::string value(line + strlen("PrivateExponent:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "PrivateExponent: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->d = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "Prime1:", strlen("Prime1:")) == 0) {
+ if (got_prime1) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Prime1 entries");
+ }
+ got_prime1 = true;
+ std::string value(line + strlen("Prime1:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Prime1: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->p = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "Prime2:", strlen("Prime2:")) == 0) {
+ if (got_prime2) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Prime2 entries");
+ }
+ got_prime2 = true;
+ std::string value(line + strlen("Prime2:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Prime2: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->q = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "Exponent1:",
+ strlen("Exponent1:")) == 0) {
+ if (got_exponent1) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Exponent1 entries");
+ }
+ got_exponent1 = true;
+ std::string value(line + strlen("Exponent1:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Exponent1: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->dmp1 = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "Exponent2:",
+ strlen("Exponent2:")) == 0) {
+ if (got_exponent2) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Exponent2 entries");
+ }
+ got_exponent2 = true;
+ std::string value(line + strlen("Exponent2:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Exponent2: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->dmq1 = BN_bin2bn(&bin[0], len, NULL);
+ } else if (strncmp(line, "Coefficient:",
+ strlen("Coefficient:")) == 0) {
+ if (got_coefficient) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Two Coefficient entries");
+ }
+ got_coefficient = true;
+ std::string value(line + strlen("Coefficient:") + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ RSA_free(rsa);
+ fclose(fp);
+ isc_throw(BadKey, "Coefficient: " << exc.what());
+ }
+ int len = static_cast<int>(bin.size());
+ rsa->iqmp = BN_bin2bn(&bin[0], len, NULL);
+ }
+ }
+ fclose(fp);
+ if (!got_algorithm) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Algorithm entry");
+ }
+ if (!got_modulus) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Modulus entry");
+ }
+ if (!got_pub_exponent) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing PublicExponent entry");
+ }
+ if (!got_priv_exponent) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing PrivateExponent entry");
+ }
+ if (!got_prime1) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Prime1 entry");
+ }
+ if (!got_prime2) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Prime2 entry");
+ }
+ if (!got_exponent1) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Exponent1 entry");
+ }
+ if (!got_exponent2) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Exponent2 entry");
+ }
+ if (!got_coefficient) {
+ RSA_free(rsa);
+ isc_throw(BadKey, "Missing Coefficient entry");
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if ((kind_ == PUBLIC) && (key_format == BASIC)) {
+ // PKCS#1 PEM file
+ // warn when password not empty
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ RSA* rsa = PEM_read_RSAPublicKey(fp, NULL, 0, NULL);
+ fclose(fp);
+ if (!rsa) {
+ isc_throw(BadKey, "PEM_read_RSAPublicKey");
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if ((kind_ == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo PEM file
+ // warn when password not empty
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ char* pwd = NULL;
+ if (!password.empty()) {
+ pwd = const_cast<char*>(password.c_str());
+ }
+ pkey_ = PEM_read_PUBKEY(fp, NULL, 0, pwd);
+ fclose(fp);
+ if (!pkey_) {
+ isc_throw(BadKey, "PEM_read_PUBKEY");
+ }
+ if (pkey_->type != EVP_PKEY_RSA) {
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(BadKey, "not a RSA Public Key");
+ }
+ } else if ((kind_ == PUBLIC) && (key_format == DNS)) {
+ // bind9 .key file (RDATA)
+ // warn when password not empty
+ if ((hash_ != MD5) && (hash_ != SHA1) &&
+ (hash_ != SHA256) && (hash_ != SHA512)) {
+ isc_throw(UnsupportedAlgorithm,
+ "Not compatible hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ char line[4096];
+ bool found = false;
+ while (fgets(line, sizeof(line), fp)) {
+ if ((line[0] == '\0') || (line[0] == ';')) {
+ continue;
+ }
+ if (strstr(line, "DNSKEY") == NULL) {
+ continue;
+ }
+ found = true;
+ if (line[strlen(line) - 1] == '\n') {
+ line[strlen(line) - 1] = 0;
+ }
+ break;
+ }
+ fclose(fp);
+ if (!found) {
+ isc_throw(BadKey, "Can't find a DNSKEY");
+ }
+ const char b64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
+ "ghijklmnopqrstuvwxyz0123456789+/=";
+ size_t last = strlen(line) - 1;
+ while (strchr(b64, static_cast<int>(line[last])) != NULL) {
+ --last;
+ }
+ const std::string value(line + last + 1);
+ std::vector<uint8_t> bin;
+ try {
+ util::encode::decodeBase64(value, bin);
+ } catch (const BadValue& exc) {
+ isc_throw(BadKey, "Can't decode base64: " << exc.what());
+ }
+ size_t key_len = bin.size();
+ const uint8_t* p = &bin[0];
+ unsigned int e_bytes = *p++;
+ --key_len;
+ if (e_bytes == 0) {
+ if (key_len < 2) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent length");
+ }
+ e_bytes = (*p++) << 8;
+ e_bytes += *p++;
+ key_len -= 2;
+ }
+ if (key_len < e_bytes) {
+ isc_throw(BadKey, "Bad RSA Public Key: short exponent");
+ }
+ if ((key_len - e_bytes) < 64) {
+ isc_throw(BadKey, "Bad RSA Public Key: too short: " <<
+ (key_len - e_bytes) * 8);
+ }
+ if ((key_len - e_bytes) > 512) {
+ isc_throw(BadKey, "Bad RSA Public Key: too large: " <<
+ (key_len - e_bytes) * 8);
+ }
+ RSA* rsa = RSA_new();
+ if (!rsa) {
+ throw std::bad_alloc();
+ }
+ rsa->e = BN_bin2bn(p, e_bytes, NULL);
+ p += e_bytes;
+ key_len -= e_bytes;
+ rsa->n = BN_bin2bn(p, key_len, NULL);
+ if (!rsa->e || !rsa->n) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ pkey_ = EVP_PKEY_new();
+ if (!pkey_) {
+ RSA_free(rsa);
+ throw std::bad_alloc();
+ }
+ if (!EVP_PKEY_set1_RSA(pkey_, rsa)) {
+ RSA_free(rsa);
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ isc_throw(LibraryError, "EVP_PKEY_set1_RSA");
+ }
+ // set1 bumped the reference counter
+ RSA_free(rsa);
+ } else if (kind_ == PUBLIC) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Public Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((kind_ == CERT) && (key_format == ASN1)) {
+ // Public Key Certificate PEM file
+ // warn when password not empty
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ x509_ = PEM_read_X509(fp, NULL, 0, NULL);
+ fclose(fp);
+ if (!x509_) {
+ isc_throw(BadKey, "PEM_read_X509");
+ }
+ int sig_nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+ if (hash_ == MD5) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.4")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA MD5 certificate");
+ }
+ } else if (hash_ == SHA1) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.5")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA1 certificate");
+ }
+ } else if (hash_ == SHA224) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.14")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA224 certificate");
+ }
+ } else if (hash_ == SHA256) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.11")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA256 certificate");
+ }
+ } else if (hash_ == SHA384) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.12")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA384 certificate");
+ }
+ } else if (hash_ == SHA512) {
+ if (sig_nid != OBJ_txt2nid("1.2.840.113549.1.1.13")) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "Require a RSA SHA512 certificate");
+ }
+ } else {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(UnsupportedAlgorithm,
+ "Bad hash algorithm for certificate: " <<
+ static_cast<int>(hash_));
+ }
+ pkey_ = X509_get_pubkey(x509_);
+ if (!pkey_) {
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "X509_get_pubkey");
+ }
+ if (pkey_->type != EVP_PKEY_RSA) {
+ EVP_PKEY_free(pkey_);
+ pkey_ = NULL;
+ X509_free(x509_);
+ x509_ = NULL;
+ isc_throw(BadKey, "not a RSA Public Key");
+ }
+ } else if (kind_ == CERT) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown Public Key Certificate format: " <<
+ static_cast<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " << static_cast<int>(kind_));
+ }
+ mdctx_.reset(new EVP_MD_CTX);
+ EVP_MD_CTX_init(mdctx_.get());
+
+ if (!EVP_DigestInit_ex(mdctx_.get(), md, NULL)) {
+ EVP_MD_CTX_cleanup(mdctx_.get());
+ EVP_PKEY_free(pkey_);
+ pkey_ =NULL;
+ isc_throw(LibraryError, "EVP_DigestInit_ex");
+ }
+}
+
+/// @brief Destructor
+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<size_t>(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<size_t>(EVP_PKEY_size(pkey_)));
+ default:
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Signature format: " <<
+ static_cast<int>(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<unsigned char> sig(size);
+ if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
+ isc_throw(LibraryError, "EVP_SignFinal");
+ }
+ if (len > size) {
+ len = size;
+ }
+ result.writeData(&sig[0], len);
+}
+
+/// @brief Calculate the final signature
+void RsaAsymImpl::sign(void* result, size_t len, const AsymFormat sig_format) {
+ unsigned int size = getSignatureLength(sig_format);
+ ossl::SecBuf<unsigned char> sig(size);
+ if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
+ isc_throw(LibraryError, "EVP_SignFinal");
+ }
+ if (len > size) {
+ len = size;
+ }
+ std::memcpy(result, &sig[0], len);
+}
+
+/// @brief Calculate the final signature
+std::vector<uint8_t> RsaAsymImpl::sign(size_t len,
+ const AsymFormat sig_format) {
+ unsigned int size = getSignatureLength(sig_format);
+ ossl::SecBuf<unsigned char> sig(size);
+ if (!EVP_SignFinal(mdctx_.get(), &sig[0], &size, pkey_)) {
+ isc_throw(LibraryError, "EVP_SignFinal");
+ }
+ // resize to min(len, size)
+ sig.resize(len < size ? len : size);
+ return (std::vector<uint8_t>(sig.begin(), sig.end()));
+}
+
+/// @brief Verify an existing signature
+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<unsigned char*>(const_cast<void*>(sig));
+ unsigned int siglen = static_cast<unsigned int>(len);
+ int status = EVP_VerifyFinal(mdctx_.get(), sigbuf, siglen, pkey_);
+ switch (status) {
+ case 1:
+ return true;
+ case 0:
+ return false;
+ case -1:
+ default:
+ isc_throw(LibraryError, "EVP_VerifyFinal");
+ }
+}
+
+/// @brief 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<int>(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<uint8_t>
+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<uint8_t> der(static_cast<size_t>(len));
+ unsigned char* p = &der[0];
+ len = i2d_RSAPrivateKey(rsa, &p);
+ RSA_free(rsa);
+ if (len != static_cast<int>(der.size())) {
+ isc_throw(LibraryError, "i2d_RSAPrivateKey");
+ }
+ return der;
+ } else if (key_kind == PRIVATE) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Private Key format: " <<
+ static_cast<int>(key_format));
+ } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
+ // PKCS#1 Public Key
+ RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+ if (!rsa) {
+ isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+ }
+ int len = i2d_RSAPublicKey(rsa, NULL);
+ if (len < 0) {
+ RSA_free(rsa);
+ isc_throw(LibraryError, "i2d_RSAPublicKey 0");
+ }
+ std::vector<uint8_t> der(static_cast<size_t>(len));
+ unsigned char* p = &der[0];
+ len = i2d_RSAPublicKey(rsa, &p);
+ RSA_free(rsa);
+ if (len != static_cast<int>(der.size())) {
+ isc_throw(LibraryError, "i2d_RSAPublicKey");
+ }
+ return der;
+ } else if ((key_kind == PUBLIC) && (key_format == ASN1)) {
+ // SubjectPublicKeyInfo
+ RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+ if (!rsa) {
+ isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+ }
+ int len = i2d_RSA_PUBKEY(rsa, NULL);
+ if (len < 0) {
+ RSA_free(rsa);
+ isc_throw(LibraryError, "i2d_RSA_PUBKEY 0");
+ }
+ std::vector<uint8_t> der(static_cast<size_t>(len));
+ unsigned char* p = &der[0];
+ len = i2d_RSA_PUBKEY(rsa, &p);
+ RSA_free(rsa);
+ if (len != static_cast<int>(der.size())) {
+ isc_throw(LibraryError, "i2d_RSA_PUBKEY");
+ }
+ return der;
+ } else if ((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<uint8_t> rdata(x_bytes + e_bytes + mod_bytes);
+ if (e_bytes < 256) {
+ rdata[0] = e_bytes;
+ } else {
+ rdata[0] = 0;
+ rdata[1] = (e_bytes >> 8) & 0xff;
+ rdata[2] = e_bytes & 0xff;
+ }
+ BN_bn2bin(rsa->e, &rdata[x_bytes]);
+ BN_bn2bin(rsa->n, &rdata[x_bytes + e_bytes]);
+ RSA_free(rsa);
+ return rdata;
+ } else if (key_kind == PUBLIC) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Public Key format: " <<
+ static_cast<int>(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<uint8_t> ber(static_cast<size_t>(len));
+ unsigned char* p = &ber[0];
+ len = i2d_X509(x509_, &p);
+ if (len != static_cast<int>(ber.size())) {
+ isc_throw(LibraryError, "i2d_X509");
+ }
+ return ber;
+ } else if (key_kind == CERT) {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown Certificate format: " <<
+ static_cast<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " <<
+ static_cast<int>(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<char*>(password.c_str());
+ enc = EVP_des_ede3_cbc();
+ if (!enc) {
+ isc_throw(LibraryError, "EVP_des_ede3_cbc");
+ }
+ }
+ FILE* fp = fopen(filename.c_str(), "w");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ if (!PEM_write_PKCS8PrivateKey(fp, pkey_, enc, pwd,
+ static_cast<int>(password.size()),
+ 0, NULL)) {
+ fclose(fp);
+ isc_throw(LibraryError, "PEM_write_PKCS8PrivateKey");
+ }
+ fclose(fp);
+ } else if ((key_kind == PRIVATE) && (key_format == DNS)) {
+ // bind9 .private file
+ if (kind_ != PRIVATE) {
+ isc_throw(UnsupportedAlgorithm, "Have no RSA Private Key");
+ }
+ if ((hash_ != MD5) && (hash_ != SHA1) &&
+ (hash_ != SHA256) && (hash_ != SHA512)) {
+ isc_throw(UnsupportedAlgorithm,
+ "Not compatible hash algorithm: " <<
+ static_cast<int>(hash_));
+ }
+ FILE* fp = fopen(filename.c_str(), "w");
+ if (!fp) {
+ isc_throw(BadKey, "Can't open file: " << filename);
+ }
+ RSA* rsa = EVP_PKEY_get1_RSA(pkey_);
+ if (!rsa) {
+ fclose(fp);
+ isc_throw(LibraryError, "EVP_PKEY_get1_RSA");
+ }
+ fprintf(fp, "Private-key-format: v1.2\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<uint8_t> 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<int>(key_format));
+ } else if ((key_kind == PUBLIC) && (key_format == BASIC)) {
+ // PKCS#1 PEM file
+ // warn when password not empty
+ FILE* fp = fopen(filename.c_str(), "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<uint8_t> 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<int>(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<int>(key_format));
+ } else {
+ isc_throw(UnsupportedAlgorithm,
+ "Unknown RSA Key kind: " <<
+ static_cast<int>(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
--- /dev/null
+// 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<uint8_t> 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<uint8_t> 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<EVP_MD_CTX> mdctx_;
+ /// @brief The raw pointer to the OpenSSL EVP_PKEY structure
+ /// There is no EVP_PKEY_init() or EVP_PKEY_cleanup() so
+ /// a smart pointer cannot be used.
+ EVP_PKEY* pkey_;
+ /// @brief The raw pointer to the OpenSSL X509 structure
+ /// There is no X509_init() or X509_cleanup() so
+ /// a smart pointer cannot be used.
+ X509* x509_;
+};
+
+} // namespace cryptolink
+} // namespace isc