diff options
Diffstat (limited to 'src/cert/cvc')
-rw-r--r-- | src/cert/cvc/cvc_ado.cpp | 119 | ||||
-rw-r--r-- | src/cert/cvc/cvc_ado.h | 95 | ||||
-rw-r--r-- | src/cert/cvc/cvc_ca.cpp | 42 | ||||
-rw-r--r-- | src/cert/cvc/cvc_ca.h | 55 | ||||
-rw-r--r-- | src/cert/cvc/cvc_cert.cpp | 100 | ||||
-rw-r--r-- | src/cert/cvc/cvc_cert.h | 98 | ||||
-rw-r--r-- | src/cert/cvc/cvc_gen_cert.h | 161 | ||||
-rw-r--r-- | src/cert/cvc/cvc_key.h | 37 | ||||
-rw-r--r-- | src/cert/cvc/cvc_req.cpp | 66 | ||||
-rw-r--r-- | src/cert/cvc/cvc_req.h | 61 | ||||
-rw-r--r-- | src/cert/cvc/cvc_self.cpp | 271 | ||||
-rw-r--r-- | src/cert/cvc/cvc_self.h | 148 | ||||
-rw-r--r-- | src/cert/cvc/eac_obj.h | 132 | ||||
-rw-r--r-- | src/cert/cvc/info.txt | 24 |
14 files changed, 1409 insertions, 0 deletions
diff --git a/src/cert/cvc/cvc_ado.cpp b/src/cert/cvc/cvc_ado.cpp new file mode 100644 index 000000000..d5083d3ab --- /dev/null +++ b/src/cert/cvc/cvc_ado.cpp @@ -0,0 +1,119 @@ +#include <botan/cvc_ado.h> +#include <fstream> +#include <assert.h> +/************************************************* + * CVC Certificate Constructor * + *************************************************/ + +namespace Botan + { + EAC1_1_ADO::EAC1_1_ADO(std::tr1::shared_ptr<DataSource> in) + { + init(in); + do_decode(); + } + EAC1_1_ADO::EAC1_1_ADO(const std::string& in) + { + std::tr1::shared_ptr<DataSource> stream(new DataSource_Stream(in, true)); + init(stream); + do_decode(); + } + void EAC1_1_ADO::force_decode() + { + SecureVector<byte> inner_cert; + BER_Decoder(tbs_bits) + .start_cons(ASN1_Tag(33)) + .raw_bytes(inner_cert) + .end_cons() + .decode(m_car) + .verify_end(); + + SecureVector<byte> req_bits = DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .raw_bytes(inner_cert) + .end_cons() + .get_contents(); + + std::tr1::shared_ptr<DataSource> req_source(new DataSource_Memory(req_bits)); + m_req = EAC1_1_Req(req_source); + sig_algo = m_req.sig_algo; + } + + MemoryVector<byte> EAC1_1_ADO::make_signed( + std::auto_ptr<PK_Signer> signer, + const MemoryRegion<byte>& tbs_bits) + { + SecureVector<byte> concat_sig = EAC1_1_obj<EAC1_1_ADO>::make_signature(signer, tbs_bits); + assert(concat_sig.size() % 2 == 0); + MemoryVector<byte> result = DER_Encoder() + .start_cons(ASN1_Tag(7), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents(); + return result; + } + ASN1_Car EAC1_1_ADO::get_car() const + { + return m_car; + } + void EAC1_1_ADO::decode_info(SharedPtrConverter<DataSource> source, SecureVector<byte> & res_tbs_bits, ECDSA_Signature & res_sig) + { + SecureVector<byte> concat_sig; + SecureVector<byte> cert_inner_bits; + ASN1_Car car; + BER_Decoder(source.get_shared()) + .start_cons(ASN1_Tag(7)) + .start_cons(ASN1_Tag(33)) + .raw_bytes(cert_inner_bits) + .end_cons() + .decode(car) + .decode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons(); + + SecureVector<byte> enc_cert = DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .raw_bytes(cert_inner_bits) + .end_cons() + .get_contents(); + SecureVector<byte> enc_car = DER_Encoder() + .encode(car) + .get_contents(); + res_tbs_bits = enc_cert; + res_tbs_bits.append(enc_car); + res_sig = decode_concatenation(concat_sig); + + + } + void EAC1_1_ADO::encode(Pipe& out, X509_Encoding encoding) const + { + SecureVector<byte> concat_sig(EAC1_1_obj<EAC1_1_ADO>::m_sig.get_concatenation()); + SecureVector<byte> der = DER_Encoder() + .start_cons(ASN1_Tag(7), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents(); + if(encoding == PEM) + throw Invalid_Argument("EAC1_1_ADO::encode() cannot PEM encode an EAC object"); + else + out.write(der); + } + SecureVector<byte> EAC1_1_ADO::tbs_data() const + { + return tbs_bits; + } + + bool EAC1_1_ADO::operator==(EAC1_1_ADO const& rhs) const + { + assert(((this->m_req == rhs.m_req) && (this->tbs_data() == rhs.tbs_data())) || + ((this->m_req != rhs.m_req) && (this->tbs_data() != rhs.tbs_data()))); + return (this->get_concat_sig() == rhs.get_concat_sig() + && this->tbs_data() == rhs.tbs_data() + && this->get_car() == rhs.get_car()); + } + EAC1_1_Req EAC1_1_ADO::get_request() const + { + return m_req; + } +}//namespace botan diff --git a/src/cert/cvc/cvc_ado.h b/src/cert/cvc/cvc_ado.h new file mode 100644 index 000000000..e2e5395b5 --- /dev/null +++ b/src/cert/cvc/cvc_ado.h @@ -0,0 +1,95 @@ +/************************************************* +* EAC1_1 CVC ADO Header File * +* (C) 2008 Falko Strenzke * +* [email protected] * +*************************************************/ + +#ifndef BOTAN_EAC_CVC_ADO_H__ +#define BOTAN_EAC_CVC_ADO_H__ + +#include <botan/x509_key.h> +#include <botan/enums.h> +#include <botan/pubkey.h> +#include <botan/ecdsa.h> +#include <botan/eac_obj.h> +#include <botan/cvc_req.h> +#include <string> + +namespace Botan { + +/** +* This class represents a TR03110 (EAC) v1.1 CVC ADO request +*/ + + // CRTP continuation from EAC1_1_obj +class EAC1_1_ADO : public EAC1_1_obj<EAC1_1_ADO> + { + friend class EAC1_1_obj<EAC1_1_ADO>; + public: + /** + * Construct a CVC ADO request from a DER encoded CVC ADO request file. + * @param str the path to the DER encoded file + */ + EAC1_1_ADO(const std::string& str); + /** + * Construct a CVC ADO request from a data source + * @param source the data source + */ + EAC1_1_ADO(std::tr1::shared_ptr<DataSource> source); + + /** + * Create a signed CVC ADO request from to be signed (TBS) data + * @param signer the signer used to sign the CVC ADO request + * @param tbs_bits the TBS data to sign + */ + static MemoryVector<byte> make_signed( + std::auto_ptr<PK_Signer> signer, + const MemoryRegion<byte>& tbs_bits); + /** + * Get the CAR of this CVC ADO request + * @result the CAR of this CVC ADO request + */ + ASN1_Car get_car() const; + + /** + * Get the CVC request contained in this object. + * @result the CVC request inside this CVC ADO request + */ + EAC1_1_Req get_request() const; + + /** + * Encode this object into a pipe. Only DER is supported. + * @param out the pipe to encode this object into + * @param encoding the encoding type to use, must be DER + */ + void encode(Pipe& out, X509_Encoding encoding) const; + + bool operator==(EAC1_1_ADO const& rhs) const; + + /** + * Get the TBS data of this CVC ADO request. + * @result the TBS data + */ + SecureVector<byte> tbs_data() const; + virtual ~EAC1_1_ADO() + {} + private: + ASN1_Car m_car; + EAC1_1_Req m_req; + + void force_decode(); + static void decode_info(SharedPtrConverter<DataSource> source, + SecureVector<byte> & res_tbs_bits, + ECDSA_Signature & res_sig); + }; + +inline bool operator!=(EAC1_1_ADO const& lhs, EAC1_1_ADO const& rhs) + { + return (!(lhs == rhs)); + } + +} + +#endif + + diff --git a/src/cert/cvc/cvc_ca.cpp b/src/cert/cvc/cvc_ca.cpp new file mode 100644 index 000000000..19b35e074 --- /dev/null +++ b/src/cert/cvc/cvc_ca.cpp @@ -0,0 +1,42 @@ +#include <botan/cvc_ca.h> +#include <botan/cvc_cert.h> +#include <botan/der_enc.h> +#include <botan/util.h> +#include <botan/oids.h> +namespace Botan { + + EAC1_1_CVC EAC1_1_CVC_CA::make_cert( + std::auto_ptr<PK_Signer> signer, + MemoryRegion<byte> const& public_key, + ASN1_Car const& car, + ASN1_Chr const& chr, + byte holder_auth_templ, + ASN1_Ced ced, + ASN1_Cex cex + ) + { + OID chat_oid(OIDS::lookup("CertificateHolderAuthorizationTemplate")); + MemoryVector<byte> enc_chat_val; + enc_chat_val.append(holder_auth_templ); + + MemoryVector<byte> enc_cpi; + enc_cpi.append(0x00); + MemoryVector<byte> tbs = DER_Encoder() + .encode(enc_cpi, OCTET_STRING, ASN1_Tag(41), APPLICATION) // cpi + .encode(car) + .raw_bytes(public_key) + .encode(chr) + .start_cons(ASN1_Tag(76), APPLICATION) + .encode(chat_oid) + .encode(enc_chat_val, OCTET_STRING, ASN1_Tag(19), APPLICATION) + .end_cons() + .encode(ced) + .encode(cex) + .get_contents(); + + MemoryVector<byte> signed_cert = EAC1_1_CVC::make_signed(signer, EAC1_1_CVC::build_cert_body(tbs)); + std::tr1::shared_ptr<DataSource> source(new DataSource_Memory(signed_cert)); + return EAC1_1_CVC(source); + } + +} diff --git a/src/cert/cvc/cvc_ca.h b/src/cert/cvc/cvc_ca.h new file mode 100644 index 000000000..590cd4c91 --- /dev/null +++ b/src/cert/cvc/cvc_ca.h @@ -0,0 +1,55 @@ +/************************************************* +* EAC1.1 CVC Certificate Authority Header File * +* (C) 1999-2007 The Botan Project * +*************************************************/ + +#ifndef BOTAN_CVC_CA_H__ +#define BOTAN_CVC_CA_H__ + +#include <botan/pkcs8.h> +#include <botan/pkcs10.h> +#include <botan/pubkey.h> +#include <botan/cvc_cert.h> +namespace Botan + { + + /** + * This class represents a CVC CA. + */ + class EAC1_1_CVC_CA + { + public: + + /** + * Create an arbitrary EAC 1.1 CVC. + * The desired key encoding must be set within the key (if applicable). + * @param signer the signer used to sign the certificate + * @param public_key the DER encoded public key to appear in + * the certificate + * @param car the CAR of the certificate + * @param chr the CHR of the certificate + * @param holder_auth_templ the holder authorization value byte to + * appear in the CHAT of the certificate + * @param ced the CED to appear in the certificate + * @param ced the CEX to appear in the certificate + */ + static EAC1_1_CVC make_cert( + std::auto_ptr<PK_Signer> signer, + MemoryRegion<byte> const& public_key, + ASN1_Car const& car, + ASN1_Chr const& chr, + byte holder_auth_templ, + ASN1_Ced ced, + ASN1_Cex cex + ); + + private: + + }; + + + + +} + +#endif diff --git a/src/cert/cvc/cvc_cert.cpp b/src/cert/cvc/cvc_cert.cpp new file mode 100644 index 000000000..29b3661ba --- /dev/null +++ b/src/cert/cvc/cvc_cert.cpp @@ -0,0 +1,100 @@ +#include <botan/tr1_mem_includer.h> +#include <botan/cvc_cert.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/pem.h> +#include <botan/parsing.h> +#include <ios> +#include <assert.h> +#include <botan/ec.h> +#include <botan/cvc_key.h> +#include <botan/oids.h> +#include <botan/look_pk.h> +namespace Botan + { + ASN1_Car EAC1_1_CVC::get_car() const + { + return m_car; + } + + ASN1_Ced EAC1_1_CVC::get_ced() const + { + return m_ced; + } + ASN1_Cex EAC1_1_CVC::get_cex() const + { + return m_cex; + } + u32bit EAC1_1_CVC::get_chat_value() const + { + return m_chat_val; + } + + /************************************************* + * Decode the TBSCertificate data * + *************************************************/ + void EAC1_1_CVC::force_decode() + { + SecureVector<byte> enc_pk; + SecureVector<byte> enc_chat_val; + u32bit cpi; + BER_Decoder tbs_cert(tbs_bits); + tbs_cert.decode(cpi, ASN1_Tag(41), APPLICATION) + .decode(m_car) + .start_cons(ASN1_Tag(73)) + .raw_bytes(enc_pk) + .end_cons() + .decode(m_chr) + .start_cons(ASN1_Tag(76)) + .decode(m_chat_oid) + .decode(enc_chat_val, OCTET_STRING, ASN1_Tag(19), APPLICATION) + .end_cons() + .decode(m_ced) + .decode(m_cex) + .verify_end(); + if(enc_chat_val.size() != 1) + { + throw Decoding_Error("CertificateHolderAuthorizationValue was not of length 1"); + } + if(cpi != 0) + { + throw Decoding_Error("EAC1_1 certificate´s cpi was not 0"); + } + ECDSA_PublicKey tmp_pk; + std::auto_ptr<EAC1_1_CVC_Decoder> dec = tmp_pk.cvc_eac1_1_decoder(); + sig_algo = dec->public_key(enc_pk); + + + m_pk = tmp_pk; + m_chat_val = enc_chat_val[0]; + self_signed = false; + if(m_car.iso_8859() == m_chr.iso_8859()) + { + self_signed= true; + } + + } + /************************************************* + * CVC Certificate Constructor * + *************************************************/ + EAC1_1_CVC::EAC1_1_CVC(std::tr1::shared_ptr<DataSource>& in) + { + init(in); + self_signed = false; + do_decode(); + } + EAC1_1_CVC::EAC1_1_CVC(const std::string& in) + { + std::tr1::shared_ptr<DataSource> stream(new DataSource_Stream(in, true)); + init(stream); + self_signed = false; + do_decode(); + } + bool EAC1_1_CVC::operator==(EAC1_1_CVC const& rhs) const + { + return (tbs_data() == rhs.tbs_data() + && get_concat_sig() == rhs.get_concat_sig()); + } + + +} diff --git a/src/cert/cvc/cvc_cert.h b/src/cert/cvc/cvc_cert.h new file mode 100644 index 000000000..00bdd6679 --- /dev/null +++ b/src/cert/cvc/cvc_cert.h @@ -0,0 +1,98 @@ +/************************************************* +* EAC1_1 CVC Header File * +* (C) 2008 Falko Strenzke * +* [email protected] * +*************************************************/ + +#ifndef BOTAN_CVC_EAC_H__ +#define BOTAN_CVC_EAC_H__ + +#include <botan/x509_key.h> +#include <botan/enums.h> +#include <botan/signed_obj.h> +#include <string> +#include <botan/pubkey.h> +#include <botan/ecdsa.h> +#include <botan/ecdsa_sig.h> +#include <botan/eac_obj.h> +#include <botan/cvc_gen_cert.h> +namespace Botan + { + + + /** + * This class represents TR03110 (EAC) v1.1 CV Certificates + */ + class EAC1_1_CVC : public EAC1_1_gen_CVC<EAC1_1_CVC>//Signed_Object + { + friend class EAC1_1_obj<EAC1_1_CVC>; + + public: + + /** + * Get the CAR of the certificate. + * @result the CAR of the certificate + */ + ASN1_Car get_car() const; + + /** + * Get the CED of this certificate. + * @result the CED this certificate + */ + ASN1_Ced get_ced() const; + /** + * Get the CEX of this certificate. + * @result the CEX this certificate + */ + ASN1_Cex get_cex() const; + + /** + * Get the CHAT value. + * @result the CHAT value + */ + u32bit get_chat_value() const; + + bool operator==(const EAC1_1_CVC&) const; + + /** + * Construct a CVC from a data source + * @param source the data source + */ + EAC1_1_CVC(std::tr1::shared_ptr<DataSource>& source); + /** + * Construct a CVC from a file + * @param str the path to the certificate file + */ + EAC1_1_CVC(const std::string& str); + virtual ~EAC1_1_CVC() + {} + + protected: + + private: + void force_decode(); + friend class EAC1_1_CVC_CA; + EAC1_1_CVC() + {} + ASN1_Car m_car; + ASN1_Ced m_ced; + ASN1_Cex m_cex; + byte m_chat_val; + OID m_chat_oid; + + + }; + + /************************************************* + *Comparison * + *************************************************/ + inline bool operator!=(EAC1_1_CVC const& lhs, EAC1_1_CVC const& rhs) + { + return !(lhs == rhs); + } + + +} + +#endif + diff --git a/src/cert/cvc/cvc_gen_cert.h b/src/cert/cvc/cvc_gen_cert.h new file mode 100644 index 000000000..7a9ac86fa --- /dev/null +++ b/src/cert/cvc/cvc_gen_cert.h @@ -0,0 +1,161 @@ +/************************************************* +* EAC1_1 general CVC Header File * +* (C) 2008 Falko Strenzke * +* [email protected] * +*************************************************/ + +#ifndef BOTAN_EAC_CVC_GCERT_H__ +#define BOTAN_EAC_CVC_GCERT_H__ + +#include <botan/x509_key.h> +#include <botan/enums.h> +#include <string> +#include <botan/pubkey.h> +#include <botan/ecdsa_sig.h> + +namespace Botan { + +/** +* This class represents TR03110 (EAC) v1.1 generalized CV Certificates +*/ +template<typename Derived> +class EAC1_1_gen_CVC : public EAC1_1_obj<Derived> // CRTP continuation from EAC1_1_obj + { + friend class EAC1_1_obj<EAC1_1_gen_CVC>; + protected: + ECDSA_PublicKey m_pk; // public key + ASN1_Chr m_chr; + bool self_signed; + + static void decode_info(SharedPtrConverter<DataSource> source, SecureVector<byte> & res_tbs_bits, ECDSA_Signature & res_sig); + public: + + /** + * Get this certificates public key. + * @result this certificates public key + */ + std::auto_ptr<Public_Key> subject_public_key() const; + + /** + * Find out whether this object is self signed. + * @result true if this object is self signed + */ + bool is_self_signed() const; + + /** + * Get the CHR of the certificate. + * @result the CHR of the certificate + */ + ASN1_Chr get_chr() const; + + /** + * Put the DER encoded version of this object into a pipe. PEM + * is not supported. + * @param out the pipe to push the DER encoded version into + * @param encoding the encoding to use. Must be DER. + */ + void encode(Pipe& out, X509_Encoding encoding) const; + + /** + * Get the to-be-signed (TBS) data of this object. + * @result the TBS data of this object + */ + SecureVector<byte> tbs_data() const; + + /** + * Build the DER encoded certifcate body of an object + * @param tbs the data to be signed + * @result the correctly encoded body of the object + */ + static SecureVector<byte> build_cert_body(MemoryRegion<byte> const& tbs); + + /** + * Create a signed generalized CVC object. + * @param signer the signer used to sign this object + * @param tbs_bits the body the generalized CVC object to be signed + * @result the DER encoded signed generalized CVC object + */ + static MemoryVector<byte> make_signed( + std::auto_ptr<PK_Signer> signer, + const MemoryRegion<byte>& tbs_bits); + virtual ~EAC1_1_gen_CVC<Derived>() + {} + + } + ; +template<typename Derived> ASN1_Chr EAC1_1_gen_CVC<Derived>::get_chr() const + { + return m_chr; + } +template<typename Derived> bool EAC1_1_gen_CVC<Derived>::is_self_signed() const + { + return self_signed; + } +template<typename Derived> MemoryVector<byte> EAC1_1_gen_CVC<Derived>::make_signed( + std::auto_ptr<PK_Signer> signer, + const MemoryRegion<byte>& tbs_bits) // static + { + SecureVector<byte> concat_sig = EAC1_1_obj<Derived>::make_signature(signer, tbs_bits); + assert(concat_sig.size() % 2 == 0); + return DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents(); + } +template<typename Derived> std::auto_ptr<Public_Key> EAC1_1_gen_CVC<Derived>::subject_public_key() const + { + return std::auto_ptr<Public_Key>(new ECDSA_PublicKey(m_pk)); + } +template<typename Derived> SecureVector<byte> EAC1_1_gen_CVC<Derived>::build_cert_body(MemoryRegion<byte> const& tbs) + { + return DER_Encoder() + .start_cons(ASN1_Tag(78), APPLICATION) + .raw_bytes(tbs) + .end_cons().get_contents(); + } +template<typename Derived> SecureVector<byte> EAC1_1_gen_CVC<Derived>::tbs_data() const + { + return build_cert_body(EAC1_1_obj<Derived>::tbs_bits); + } +template<typename Derived> void EAC1_1_gen_CVC<Derived>::encode(Pipe& out, X509_Encoding encoding) const + { + SecureVector<byte> concat_sig(EAC1_1_obj<Derived>::m_sig.get_concatenation()); + SecureVector<byte> der = DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .start_cons(ASN1_Tag(78), APPLICATION) + .raw_bytes(EAC1_1_obj<Derived>::tbs_bits) + .end_cons() + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents(); + + if (encoding == PEM) + throw Invalid_Argument("EAC1_1_gen_CVC::encode() cannot PEM encode an EAC object"); + else + out.write(der); + } + +template<typename Derived> +void EAC1_1_gen_CVC<Derived>::decode_info( + SharedPtrConverter<DataSource> source, + SecureVector<byte> & res_tbs_bits, + ECDSA_Signature & res_sig) + { + SecureVector<byte> concat_sig; + BER_Decoder(source.get_shared()) + .start_cons(ASN1_Tag(33)) + .start_cons(ASN1_Tag(78)) + .raw_bytes(res_tbs_bits) + .end_cons() + .decode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons(); + res_sig = ecdsa::decode_concatenation(concat_sig); + } + +} + +#endif + + diff --git a/src/cert/cvc/cvc_key.h b/src/cert/cvc/cvc_key.h new file mode 100644 index 000000000..1867f04f4 --- /dev/null +++ b/src/cert/cvc/cvc_key.h @@ -0,0 +1,37 @@ +/************************************************* +* EAC CVC Public Key Header File * +* (C) 2008 FlexSecure Gmbh * +* Falko Strenzke * +* [email protected] * +*************************************************/ + +#ifndef BOTAN_EAC1_1_CVC_PUBLIC_KEY_H__ +#define BOTAN_EAC1_1_CVC_PUBLIC_KEY_H__ + +#include <botan/pipe.h> +#include <botan/pk_keys.h> +#include <botan/alg_id.h> + +namespace Botan { + +/************************************************* + * EAC CVC Public Key Encoder * +*************************************************/ + class EAC1_1_CVC_Encoder + { + public: + virtual MemoryVector<byte> public_key(AlgorithmIdentifier const&) const = 0; + virtual ~EAC1_1_CVC_Encoder() {} + }; + +/************************************************* + * EAC CVC Public Key Decoder * +*************************************************/ + class EAC1_1_CVC_Decoder + { + public: + virtual AlgorithmIdentifier const public_key(const MemoryRegion<byte>&) = 0; + virtual ~EAC1_1_CVC_Decoder() {} + }; +} +#endif diff --git a/src/cert/cvc/cvc_req.cpp b/src/cert/cvc/cvc_req.cpp new file mode 100644 index 000000000..2d5172917 --- /dev/null +++ b/src/cert/cvc/cvc_req.cpp @@ -0,0 +1,66 @@ +#include <botan/tr1_mem_includer.h> +#include <botan/cvc_cert.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/pem.h> +#include <botan/parsing.h> +#include <ios> +#include <assert.h> +#include <botan/ec.h> +#include <botan/cvc_key.h> +#include <botan/oids.h> +#include <botan/look_pk.h> +#include <botan/cvc_req.h> +namespace Botan +{ + + + + bool EAC1_1_Req::operator==(EAC1_1_Req const& rhs) const + { + return (this->tbs_data() == rhs.tbs_data() + && this->get_concat_sig() == rhs.get_concat_sig()); + } + + /************************************************* + * Decode the TBSCertificate data * + *************************************************/ + void EAC1_1_Req::force_decode() + { + SecureVector<byte> enc_pk; + BER_Decoder tbs_cert(tbs_bits); + u32bit cpi; + tbs_cert.decode(cpi, ASN1_Tag(41), APPLICATION) + .start_cons(ASN1_Tag(73)) + .raw_bytes(enc_pk) + .end_cons() + .decode(m_chr) + .verify_end(); + if(cpi != 0) + { + throw Decoding_Error("EAC1_1 request´s cpi was not 0"); + } + ECDSA_PublicKey tmp_pk; + std::auto_ptr<EAC1_1_CVC_Decoder> dec = tmp_pk.cvc_eac1_1_decoder(); + sig_algo = dec->public_key(enc_pk); + m_pk = tmp_pk; + } + /************************************************* + * X509_Certificate Constructor * + *************************************************/ + EAC1_1_Req::EAC1_1_Req(std::tr1::shared_ptr<DataSource> in) + { + init(in); + self_signed = true; + do_decode(); + } + EAC1_1_Req::EAC1_1_Req(const std::string& in) + { + std::tr1::shared_ptr<DataSource> stream(new DataSource_Stream(in, true)); + init(stream); + self_signed = true; + do_decode(); + } + + +} diff --git a/src/cert/cvc/cvc_req.h b/src/cert/cvc/cvc_req.h new file mode 100644 index 000000000..674598a20 --- /dev/null +++ b/src/cert/cvc/cvc_req.h @@ -0,0 +1,61 @@ +/************************************************* +* EAC1_1 CVC Request Header File * +* (C) 2008 Falko Strenzke * +* [email protected] * +*************************************************/ + +#ifndef BOTAN_EAC_CVC_REQ_H__ +#define BOTAN_EAC_CVC_REQ_H__ + +#include <botan/x509_key.h> +#include <botan/enums.h> +#include <botan/cvc_gen_cert.h> +#include <botan/cvc_req.h> +namespace Botan +{ + + /** + * This class represents TR03110 v1.1 EAC CV Certificate Requests. + */ + class EAC1_1_Req : public EAC1_1_gen_CVC<EAC1_1_Req> + { + friend class EAC1_1_Req_CA; + friend class EAC1_1_ADO; + friend class EAC1_1_obj<EAC1_1_Req>; + + public: + bool operator==(const EAC1_1_Req&) const; + /** + * Construct a CVC request from a data source. + * @param source the data source + */ + EAC1_1_Req(std::tr1::shared_ptr<DataSource> source); + /** + * Construct a CVC request from a DER encoded CVC reqeust file. + * @param str the path to the DER encoded file + */ + EAC1_1_Req(const std::string& str); + virtual ~EAC1_1_Req(){} + + protected: + + private: + void force_decode(); + EAC1_1_Req() + {} + + }; + + /************************************************* + *Comparison * + *************************************************/ + inline bool operator!=(EAC1_1_Req const& lhs, EAC1_1_Req const& rhs) + { + return !(lhs == rhs); + } + + +} + +#endif // h-guard + diff --git a/src/cert/cvc/cvc_self.cpp b/src/cert/cvc/cvc_self.cpp new file mode 100644 index 000000000..130ac6998 --- /dev/null +++ b/src/cert/cvc/cvc_self.cpp @@ -0,0 +1,271 @@ +#include <botan/cvc_self.h> +#include <botan/cvc_cert.h> +#include <botan/cvc_ca.h> +#include <botan/alg_id.h> +#include <botan/cvc_key.h> +#include <botan/oids.h> +#include <botan/look_pk.h> +#include <botan/cvc_req.h> +#include <botan/cvc_ado.h> +#include <botan/util.h> +#include <botan/config.h> +#include <botan/ec.h> +#include <sstream> +using namespace Botan; +namespace Botan + { + namespace + { + + std::string padding_and_hash_from_oid(OID const& oid) + { + std::string padding_and_hash = OIDS::lookup(oid); // use the hash + assert(padding_and_hash.substr(0,6) == "ECDSA/"); // can only be ECDSA for now + assert(padding_and_hash.find("/",0) == 5); + padding_and_hash.erase(0, padding_and_hash.find("/",0) + 1); + return padding_and_hash; + } + std::string fixed_len_seqnr(u32bit seqnr, u32bit len) + { + std::stringstream ss; + std::string result; + ss << seqnr; + ss >> result; + if (result.size() > len) + { + throw Invalid_Argument("fixed_len_seqnr(): number too high to be encoded in provided length"); + } + while (result.size() < len) + { + result.insert(0,"0"); + } + return result; + } + + } + namespace CVC_EAC + { + + EAC1_1_CVC create_self_signed_cert(Private_Key const& key, + EAC1_1_CVC_Options const& opt) + { + // NOTE: we ignore + // the value + // of opt.chr + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + + ASN1_Chr chr(opt.car.value()); + + AlgorithmIdentifier sig_algo; + std::string padding_and_hash(eac_cvc_emsa + "(" + opt.hash_alg + ")"); + sig_algo.oid = OIDS::lookup_bsi(priv_key->algo_name() + "/" + padding_and_hash); + sig_algo = AlgorithmIdentifier(sig_algo.oid, AlgorithmIdentifier::USE_NULL_PARAM); + + std::auto_ptr<Botan::PK_Signer> signer = get_pk_signer(*priv_key, padding_and_hash); + + std::auto_ptr<EAC1_1_CVC_Encoder> enc(priv_key->cvc_eac1_1_encoder()); + MemoryVector<byte> enc_public_key = enc->public_key(sig_algo); + return EAC1_1_CVC_CA::make_cert(signer, enc_public_key, opt.car, chr, opt.holder_auth_templ, opt.ced, opt.cex); + + } + + EAC1_1_Req create_cvc_req(Private_Key const& key, + ASN1_Chr const& chr, + std::string const& hash_alg) + { + + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + AlgorithmIdentifier sig_algo; + std::string padding_and_hash(eac_cvc_emsa + "(" + hash_alg + ")"); + sig_algo.oid = OIDS::lookup_bsi(priv_key->algo_name() + "/" + padding_and_hash); + sig_algo = AlgorithmIdentifier(sig_algo.oid, AlgorithmIdentifier::USE_NULL_PARAM); + + std::auto_ptr<Botan::PK_Signer> signer = get_pk_signer(*priv_key, padding_and_hash); + + std::auto_ptr<EAC1_1_CVC_Encoder> enc(priv_key->cvc_eac1_1_encoder()); + MemoryVector<byte> enc_public_key = enc->public_key(sig_algo); + MemoryVector<byte> enc_cpi; + enc_cpi.append(0x00); + MemoryVector<byte> tbs = DER_Encoder() + .encode(enc_cpi, OCTET_STRING, ASN1_Tag(41), APPLICATION) + .raw_bytes(enc_public_key) + .encode(chr) + .get_contents(); + + MemoryVector<byte> signed_cert = EAC1_1_gen_CVC<EAC1_1_Req>::make_signed(signer, EAC1_1_gen_CVC<EAC1_1_Req>::build_cert_body(tbs)); + std::tr1::shared_ptr<DataSource> source(new DataSource_Memory(signed_cert)); + return EAC1_1_Req(source); + } + + EAC1_1_ADO create_ado_req(Private_Key const& key, + EAC1_1_Req const& req, + ASN1_Car const& car) + { + + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + std::string padding_and_hash = padding_and_hash_from_oid(req.signature_algorithm().oid); + std::auto_ptr<Botan::PK_Signer> signer = get_pk_signer(*priv_key, padding_and_hash); + SecureVector<byte> tbs_bits = req.BER_encode(); + tbs_bits.append(DER_Encoder().encode(car).get_contents()); + MemoryVector<byte> signed_cert = EAC1_1_ADO::make_signed(signer, tbs_bits); + std::tr1::shared_ptr<DataSource> source(new DataSource_Memory(signed_cert)); + return EAC1_1_ADO(source); + } + + } // namespace CVC_EAC + namespace DE_EAC + { + EAC1_1_CVC create_cvca(Private_Key const& key, std::string const& hash, ASN1_Car const& car, bool iris, bool fingerpr) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + EAC1_1_CVC_Options opts; + opts.car = car; + const u64bit current_time = system_time(); + + opts.ced = ASN1_Ced(current_time); + opts.cex = ASN1_Cex(opts.ced); + opts.cex.add_months(global_config().option_as_u32bit("eac/ca/cvca_validity_months")); + opts.holder_auth_templ = (CVCA | (iris * IRIS) | (fingerpr * FINGERPRINT)); + opts.hash_alg = hash; + return Botan::CVC_EAC::create_self_signed_cert(*priv_key, opts); + } + + + + EAC1_1_CVC link_cvca(EAC1_1_CVC const& signer, + Private_Key const& key, + EAC1_1_CVC const& signee) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + ASN1_Ced ced(system_time()); + ASN1_Cex cex(signee.get_cex()); + if (*static_cast<EAC_Time*>(&ced) > *static_cast<EAC_Time*>(&cex)) + { + std::string detail("link_cvca(): validity periods of provided certificates don´t overlap: currend time = ced = "); + detail += ced.as_string(); + detail += ", signee.cex = "; + detail += cex.as_string(); + throw Invalid_Argument(detail); + } + if (signer.signature_algorithm() != signee.signature_algorithm()) + { + throw Invalid_Argument("link_cvca(): signature algorithms of signer and signee don´t match"); + } + AlgorithmIdentifier sig_algo = signer.signature_algorithm(); + std::string padding_and_hash = padding_and_hash_from_oid(sig_algo.oid); + std::auto_ptr<Botan::PK_Signer> pk_signer = get_pk_signer(*priv_key, padding_and_hash); + std::auto_ptr<Public_Key> pk = signee.subject_public_key(); + ECDSA_PublicKey* subj_pk = dynamic_cast<ECDSA_PublicKey*>(pk.get()); + subj_pk->set_parameter_encoding(ENC_EXPLICIT); + std::auto_ptr<EAC1_1_CVC_Encoder> enc(subj_pk->cvc_eac1_1_encoder()); + MemoryVector<byte> enc_public_key = enc->public_key(sig_algo); + return EAC1_1_CVC_CA::make_cert(pk_signer, enc_public_key, + signer.get_car(), + signee.get_chr(), + signer.get_chat_value(), + ced, + cex); + } + + EAC1_1_CVC sign_request(EAC1_1_CVC const& signer_cert, + Private_Key const& key, + EAC1_1_Req const& signee, + u32bit seqnr, + u32bit seqnr_len, + bool domestic) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + std::string chr_str = signee.get_chr().value(); + chr_str.append(fixed_len_seqnr(seqnr, seqnr_len)); + ASN1_Chr chr(chr_str); + std::string padding_and_hash = padding_and_hash_from_oid(signee.signature_algorithm().oid); + std::auto_ptr<Botan::PK_Signer> pk_signer = get_pk_signer(*priv_key, padding_and_hash); + std::auto_ptr<Public_Key> pk = signee.subject_public_key(); + ECDSA_PublicKey* subj_pk = dynamic_cast<ECDSA_PublicKey*>(pk.get()); + std::auto_ptr<Public_Key> signer_pk = signer_cert.subject_public_key(); + + // for the case that the domain parameters are not set... + // (we use those from the signer because they must fit) + subj_pk->set_domain_parameters(priv_key->get_domain_parameters()); + + subj_pk->set_parameter_encoding(ENC_IMPLICITCA); + std::auto_ptr<EAC1_1_CVC_Encoder> enc(subj_pk->cvc_eac1_1_encoder()); + AlgorithmIdentifier sig_algo(signer_cert.signature_algorithm()); + MemoryVector<byte> enc_public_key = enc->public_key(sig_algo); + const u64bit current_time = system_time(); + ASN1_Ced ced(current_time); + u32bit chat_val; + u32bit chat_low = signer_cert.get_chat_value() & 0x3; // take the chat rights from signer + ASN1_Cex cex(ced); + if ((signer_cert.get_chat_value() & CVCA) == CVCA) + { + // we sign a dvca + cex.add_months(global_config().option_as_u32bit("eac/ca/dvca_validity_months")); + if (domestic) + { + chat_val = DVCA_domestic | chat_low; + } + else + { + chat_val = DVCA_foreign | chat_low; + } + } + else if ((signer_cert.get_chat_value() & DVCA_domestic) == DVCA_domestic || + (signer_cert.get_chat_value() & DVCA_foreign) == DVCA_foreign) + { + cex.add_months(global_config().option_as_u32bit("eac/ca/is_validity_months")); + chat_val = IS | chat_low; + } + else + { + throw Invalid_Argument("sign_request(): encountered illegal value for CHAT"); + // (IS cannot sign certificates) + } + return EAC1_1_CVC_CA::make_cert(pk_signer, enc_public_key, + ASN1_Car(signer_cert.get_chr().iso_8859()), + chr, + chat_val, + ced, + cex); + } + EAC1_1_Req create_cvc_req(Private_Key const& prkey, + ASN1_Chr const& chr, + std::string const& hash_alg) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&prkey); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + ECDSA_PrivateKey key(*priv_key); + key.set_parameter_encoding(ENC_IMPLICITCA); + return Botan::CVC_EAC::create_cvc_req(key, chr, hash_alg); + } + + } // namespace DE_EAC + } diff --git a/src/cert/cvc/cvc_self.h b/src/cert/cvc/cvc_self.h new file mode 100644 index 000000000..7caa96832 --- /dev/null +++ b/src/cert/cvc/cvc_self.h @@ -0,0 +1,148 @@ +/************************************************* +* X.509 Self-Signed Certificate Header File * +* (C) 1999-2007 The Botan Project * +*************************************************/ + +#ifndef BOTAN_CVC_EAC_SELF_H__ +#define BOTAN_CVC_EAC_SELF_H__ + +#include <botan/x509cert.h> +#include <botan/pkcs8.h> +#include <botan/pkcs10.h> +#include <botan/cvc_cert.h> +#include <botan/ec.h> +#include <botan/asn1_obj.h> +#include <botan/cvc_req.h> +#include <botan/cvc_ado.h> +namespace Botan + { + + /** + * This class represents a set of options used for the creation of CVC certificates + */ + class EAC1_1_CVC_Options + { + public: + + ASN1_Car car; + ASN1_Chr chr; + byte holder_auth_templ; + ASN1_Ced ced; + ASN1_Cex cex; + std::string hash_alg; + }; +/** +* This namespace represents general EAC 1.1 convenience functions. +*/ + namespace CVC_EAC + { + + /** + * Create a selfsigned CVCA + * @param key the ECDSA private key to be used to sign the certificate + * @param opts used to set several parameters. Necessary are: + * car, holder_auth_templ, hash_alg, ced, cex and hash_alg + * @result the self signed certificate + */ + +EAC1_1_CVC create_self_signed_cert(Private_Key const& key, + EAC1_1_CVC_Options const& opts); + /** + * Create a CVC request. The key encoding will be according to the provided private key. + * @param priv_key the private key associated with the requesting entity + * @param chr the chr to appear in the certificate (to be provided without + * sequence number) + * @param hash_alg the string defining the hash algorithm to be used for the creation + * of the signature + * @result the new request + */ + EAC1_1_Req create_cvc_req(Private_Key const& priv_key, + ASN1_Chr const& chr, + std::string const& hash_alg); + + /** + * Create an ADO from a request object. + * @param priv_key the private key used to sign the ADO + * @param req the request forming the body of the ADO + * @param car the CAR forming the body of the ADO, i.e. the + * CHR of the entity associated with the provided private key + */ + EAC1_1_ADO create_ado_req(Private_Key const& priv_key, + EAC1_1_Req const& req, + ASN1_Car const& car); + } +/** +* This namespace represents EAC 1.1 CVC convenience functions following the specific german +* requirements. +*/ + namespace DE_EAC + { + /** + * Create a CVCA certificate. + * @param priv_key the private key associated with the CVCA certificate + * to be created + * @param hash the string identifying the hash algorithm to be used + * for signing the certificate to be created + * @param car the CAR of the certificate to be created + * @param iris indicates whether the entity associated with the certificate + * shall be entitled to read the biometrical iris image + * @param fingerpr indicates whether the entity associated with the certificate + * shall be entitled to read the biometrical fingerprint image + * @result the CVCA certificate created + */ + EAC1_1_CVC create_cvca(Private_Key const& priv_key, + std::string const& hash, + ASN1_Car const& car, + bool iris, + bool fingerpr); + + /** + * Create a link certificate between two CVCA certificates. The key + * encoding will be implicitCA. + * @param signer the cvca certificate associated with the signing + * entity + * @param priv_key the private key associated with the signer + * @param to_be_signed the certificate which whose CAR/CHR will be + * the holder of the link certificate + */ + EAC1_1_CVC link_cvca(EAC1_1_CVC const& signer, + Private_Key const& priv_key, + EAC1_1_CVC const& to_be_signed); + + /** + * Create a CVC request. The key encoding will be implicitCA. + * @param priv_key the private key associated with the requesting entity + * @param chr the chr to appear in the certificate (to be provided without + * sequence number) + * @param hash_alg the string defining the hash algorithm to be used for the creation + * of the signature + * @result the new request + */ + EAC1_1_Req create_cvc_req(Private_Key const& priv_key, + ASN1_Chr const& chr, + std::string const& hash_alg); + /** + * Sign a CVC request. + * @param signer_cert the certificate of the signing entity + * @param priv_key the private key of the signing entity + * @param req the request to be signed + * @param seqnr the sequence number of the certificate to be created + * @param seqnr_len the number of digits the sequence number will be + * encoded in + * @param domestic indicates whether to sign a domestic or a foreign certificate: + * set to true for domestic + * @result the new certificate + * + **/ + EAC1_1_CVC sign_request(EAC1_1_CVC const& signer_cert, + Private_Key const& priv_key, + EAC1_1_Req const& req, + u32bit seqnr, + u32bit seqnr_len, + bool domestic); + } + + + } + +#endif diff --git a/src/cert/cvc/eac_obj.h b/src/cert/cvc/eac_obj.h new file mode 100644 index 000000000..5b1912595 --- /dev/null +++ b/src/cert/cvc/eac_obj.h @@ -0,0 +1,132 @@ +/************************************************* +* EAC1_1 objects Header File * +* (C) 2008 Falko Strenzke * +* [email protected] * +*************************************************/ + +#ifndef BOTAN_EAC_OBJ_H__ +#define BOTAN_EAC_OBJ_H__ + +#include <botan/pubkey.h> +#include <botan/x509_key.h> +#include <botan/x509_obj.h> +#include <botan/enums.h> +#include <botan/pubkey.h> +#include <botan/parsing.h> +#include <botan/pem.h> +#include <botan/oids.h> +#include <botan/look_pk.h> +#include <botan/ecdsa_sig.h> +#include <string> + +namespace Botan { + +const std::string eac_cvc_emsa("EMSA1_BSI"); + +/************************************************* +* TR03110 v1.1 EAC CV Certificate * +*************************************************/ +template<typename Derived> +class EAC1_1_obj : public X509_Object // CRTP is used enable the call sequence: + { + // data members first: + protected: + + ECDSA_Signature m_sig; + + + // member functions here: + public: + /** + * Return the signature as a concatenation of the encoded parts. + * @result the concatenated signature + */ + SecureVector<byte> get_concat_sig() const; + + /** + * Verify the signature of this objects. + * @param pub_key the public key to verify the signature with + * @result true if the verification succeeded + */ + virtual bool check_signature(Public_Key& pub_key) const; + + protected: + void init(SharedPtrConverter<DataSource> in); + + static SecureVector<byte> make_signature(const PK_Signer* signer, + const MemoryRegion<byte>& tbs_bits, + RandomNumberGenerator& rng); + + virtual ~EAC1_1_obj<Derived>(){} + + }; + +template<typename Derived> SecureVector<byte> EAC1_1_obj<Derived>::get_concat_sig() const + { + return m_sig.get_concatenation(); + } +template<typename Derived> SecureVector<byte> EAC1_1_obj<Derived>::make_signature(const PK_Signer* signer, + const MemoryRegion<byte>& tbs_bits, + RandomNumberGenerator& rng) + { + SecureVector<byte> seq_sig = signer->sign_message(tbs_bits, rng); // this is the signature as a der sequence + ECDSA_Signature sig(decode_seq(seq_sig)); + SecureVector<byte> concat_sig(sig.get_concatenation()); + return concat_sig; + } + +template<typename Derived> void EAC1_1_obj<Derived>::init(SharedPtrConverter<DataSource> in) + { + + try + { + Derived::decode_info(in.get_shared(), tbs_bits, m_sig); + } + catch(Decoding_Error) + { + throw Decoding_Error(PEM_label_pref + " decoding failed"); + } + } + +template<typename Derived> bool EAC1_1_obj<Derived>::check_signature(Public_Key& pub_key) const + { + try + { + std::vector<std::string> sig_info = + split_on(OIDS::lookup(sig_algo.oid), '/'); + + if(sig_info.size() != 2 || sig_info[0] != pub_key.algo_name()) + { + return false; + } + + std::string padding = sig_info[1]; + Signature_Format format = + (pub_key.message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363; + + std::auto_ptr<PK_Verifier> verifier; + if(dynamic_cast<PK_Verifying_wo_MR_Key*>(&pub_key)) + { + PK_Verifying_wo_MR_Key& sig_key = + dynamic_cast<PK_Verifying_wo_MR_Key&>(pub_key); + verifier.reset(get_pk_verifier(sig_key, padding, format)); + } + else + { + return false; + } + std::auto_ptr<ECDSA_Signature_Encoder> enc = m_sig.x509_encoder(); + SecureVector<byte> seq_sig = enc->signature_bits(); + SecureVector<byte> to_sign = tbs_data(); + return verifier->verify_message(to_sign, seq_sig); + + } + catch(...) + { + return false; + } + } + +} + +#endif diff --git a/src/cert/cvc/info.txt b/src/cert/cvc/info.txt new file mode 100644 index 000000000..6af8ff4e5 --- /dev/null +++ b/src/cert/cvc/info.txt @@ -0,0 +1,24 @@ +realname "Card Verifiable Certificates" + +define CARD_VERIFIABLE_CERTIFICATES + +load_on request + +<requires> +</requires> + +<add> +cvc_ado.cpp +cvc_ado.h +cvc_ca.cpp +cvc_ca.h +cvc_cert.cpp +cvc_cert.h +cvc_gen_cert.h +cvc_key.h +cvc_req.cpp +cvc_req.h +cvc_self.cpp +cvc_self.h +eac_obj.h +</add> |