1#include <vanetza/security/persistence.hpp>
2#include <vanetza/security/key_type.hpp>
6#ifdef VANETZA_WITH_OPENSSL
7#include <vanetza/security/openssl_wrapper.hpp>
10#include <openssl/objects.h>
11#include <openssl/pem.h>
14#ifdef VANETZA_WITH_CRYPTOPP
15#include <cryptopp/base64.h>
16#include <cryptopp/eccrypto.h>
17#include <cryptopp/files.h>
18#include <cryptopp/integer.h>
19#include <cryptopp/oids.h>
20#include <cryptopp/osrng.h>
21#include <cryptopp/queue.h>
22#include <cryptopp/sha.h>
30#ifdef VANETZA_WITH_OPENSSL
34KeyType key_type_from_nid(
int nid)
37 case NID_X9_62_prime256v1:
38 return KeyType::NistP256;
39 case NID_brainpoolP256r1:
40 return KeyType::BrainpoolP256r1;
41 case NID_brainpoolP384r1:
42 return KeyType::BrainpoolP384r1;
44 return KeyType::Unspecified;
50 openssl::
Key ec_key(EVP_PKEY_get1_EC_KEY(pkey));
52 throw std::runtime_error(
"Key is not an EC key");
55 const EC_GROUP* group = EC_KEY_get0_group(ec_key);
56 const KeyType type = key_type_from_nid(group ? EC_GROUP_get_curve_name(group) : NID_undef);
57 if (type == KeyType::Unspecified) {
58 throw std::runtime_error(
"Unsupported EC curve in private key");
61 const BIGNUM* priv_bn = EC_KEY_get0_private_key(ec_key);
63 throw std::runtime_error(
"EC key has no private component");
68 key.key.resize(key_length(type));
69 if (BN_bn2binpad(priv_bn, key.key.data(), key.key.size()) < 0) {
70 throw std::runtime_error(
"private key does not fit the curve length");
77PrivateKey load_private_key_from_pem_file_openssl(
const std::string& key_path)
79 openssl::
Bio bio(BIO_new_file(key_path.c_str(),
"rb"));
81 throw std::runtime_error(
"Cannot open key file: " + key_path);
83 openssl::
EvpKey pkey(PEM_read_bio_PrivateKey(bio,
nullptr,
nullptr,
nullptr));
85 throw std::runtime_error(
"Failed to load PEM private key from: " + key_path);
87 return extract_private_key(pkey);
90PrivateKey load_private_key_from_der_file_openssl(
const std::string& key_path)
92 openssl::
Bio bio(BIO_new_file(key_path.c_str(),
"rb"));
94 throw std::runtime_error(
"Cannot open key file: " + key_path);
96 openssl::
EvpKey pkey(d2i_PrivateKey_bio(bio,
nullptr));
98 throw std::runtime_error(
"Failed to load DER private key from: " + key_path);
100 return extract_private_key(pkey);
104#ifdef VANETZA_WITH_CRYPTOPP
108using EcPrivateKey = CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PrivateKey;
109using EcGroupParameters = CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP>;
111KeyType detect_curve(
const EcGroupParameters& gp)
113 KeyType key_type = KeyType::Unspecified;
115 if (EcGroupParameters(CryptoPP::ASN1::secp256r1()) == gp) {
116 key_type = KeyType::NistP256;
117 }
else if (EcGroupParameters(CryptoPP::ASN1::brainpoolP256r1()) == gp) {
118 key_type = KeyType::BrainpoolP256r1;
119 }
else if (EcGroupParameters(CryptoPP::ASN1::brainpoolP384r1()) == gp) {
120 key_type = KeyType::BrainpoolP384r1;
126PrivateKey extract_private_key(CryptoPP::BufferedTransformation& der)
128 CryptoPP::AutoSeededRandomPool rng;
129 EcPrivateKey private_key;
130 private_key.Load(der);
131 if (!private_key.Validate(rng, 3)) {
132 throw std::runtime_error(
"Private key validation failed");
135 const KeyType type = detect_curve(private_key.GetGroupParameters());
136 if (type == KeyType::Unspecified) {
137 throw std::runtime_error(
"Unsupported EC curve in private key");
142 key.key.resize(key_length(type));
143 private_key.GetPrivateExponent().Encode(key.key.data(), key.key.size());
147void pem_decode(std::istream& in, CryptoPP::BufferedTransformation& dest)
149 static const std::string HEADER =
"-----BEGIN PRIVATE KEY-----";
150 static const std::string FOOTER =
"-----END PRIVATE KEY-----";
153 while (std::getline(in, line)) {
154 if (line.find(HEADER) != std::string::npos) {
159 throw std::runtime_error(
"PEM header not found");
162 CryptoPP::Base64Decoder decoder;
163 decoder.Attach(
new CryptoPP::Redirector(dest));
165 while (std::getline(in, line)) {
166 if (line.find(FOOTER) != std::string::npos) {
169 decoder.Put(
reinterpret_cast<
const uint8_t*>(line.data()), line.length());
172 throw std::runtime_error(
"PEM footer not found");
175 decoder.MessageEnd();
180PrivateKey load_private_key_from_pem_file_cryptopp(
const std::string& key_path)
182 std::ifstream file(key_path);
184 throw std::runtime_error(
"Cannot open key file: " + key_path);
186 CryptoPP::ByteQueue der;
187 pem_decode(file, der);
188 return extract_private_key(der);
191PrivateKey load_private_key_from_der_file_cryptopp(
const std::string& key_path)
194 CryptoPP::FileSource source(key_path.c_str(),
true);
195 return extract_private_key(source);
196 }
catch (
const CryptoPP::FileStore::OpenErr&) {
197 throw std::runtime_error(
"Cannot open key file: " + key_path);
202PrivateKey load_private_key_from_pem_file(
const std::string& key_path)
204#if defined(VANETZA_WITH_OPENSSL
)
205 return load_private_key_from_pem_file_openssl(key_path);
206#elif defined(VANETZA_WITH_CRYPTOPP)
207 return load_private_key_from_pem_file_cryptopp(key_path);
209# warning "no crypto backend available for persistence"
210 return PrivateKey {};
214PrivateKey load_private_key_from_der_file(
const std::string& key_path)
216#if defined(VANETZA_WITH_OPENSSL
)
217 return load_private_key_from_der_file_openssl(key_path);
218#elif defined(VANETZA_WITH_CRYPTOPP)
219 return load_private_key_from_der_file_cryptopp(key_path);
221# warning "no crypto backend available for persistence"
222 return PrivateKey {};