From 858fcca3a53da84a306f55d1811bf119bf8d862f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20D=C3=B6rre?= Date: Sun, 2 Nov 2014 01:32:56 +0100 Subject: [PATCH] add: Add support for Subject Alternative Names (SANs) --- src/X509.cpp | 70 ++++++++++++++++++++++++++++++++++++- src/X509.h | 8 +++-- src/database.h | 8 +++++ src/main.cpp | 7 ++++ src/mysql.cpp | 32 +++++++++++++++-- src/simpleOpensslSigner.cpp | 9 +++-- 6 files changed, 125 insertions(+), 9 deletions(-) diff --git a/src/X509.cpp b/src/X509.cpp index 2e441b2..5d38e3c 100644 --- a/src/X509.cpp +++ b/src/X509.cpp @@ -1,5 +1,7 @@ #include "X509.h" +#include + #include #include #include @@ -95,7 +97,40 @@ void X509Cert::setTimes( long before, long after ) { X509_gmtime_adj( X509_get_notAfter( target.get() ), after ); } -void X509Cert::setExtensions( std::shared_ptr caCert ) { +static X509_EXTENSION* do_ext_i2d( int ext_nid, int crit, ASN1_VALUE* ext_struc ) { + unsigned char* ext_der; + int ext_len; + ASN1_OCTET_STRING* ext_oct; + X509_EXTENSION* ext; + /* Convert internal representation to DER */ + ext_der = NULL; + ext_len = ASN1_item_i2d( ext_struc, &ext_der, ASN1_ITEM_ptr( ASN1_ITEM_ref( GENERAL_NAMES ) ) ); + + if( ext_len < 0 ) { + goto merr; + } + + if( !( ext_oct = M_ASN1_OCTET_STRING_new() ) ) { + goto merr; + } + + ext_oct->data = ext_der; + ext_oct->length = ext_len; + + ext = X509_EXTENSION_create_by_NID( NULL, ext_nid, crit, ext_oct ); + + if( !ext ) { + goto merr; + } + + M_ASN1_OCTET_STRING_free( ext_oct ); + return ext; + +merr: + throw "memerr"; +} + +void X509Cert::setExtensions( std::shared_ptr caCert, std::vector>& sans ) { add_ext( caCert, target, NID_basic_constraints, "critical,CA:FALSE" ); add_ext( caCert, target, NID_subject_key_identifier, "hash" ); add_ext( caCert, target, NID_authority_key_identifier, "keyid,issuer:always" ); @@ -103,6 +138,38 @@ void X509Cert::setExtensions( std::shared_ptr caCert ) { add_ext( caCert, target, NID_ext_key_usage, "clientAuth, serverAuth" ); add_ext( caCert, target, NID_info_access, "OCSP;URI:http://ocsp.cacert.org" ); add_ext( caCert, target, NID_crl_distribution_points, "URI:http://crl.cacert.org/class3-revoke.crl" ); + + std::shared_ptr gens = std::shared_ptr( + sk_GENERAL_NAME_new_null(), + []( GENERAL_NAMES * ref ) { + if( ref ) { + sk_GENERAL_NAME_pop_free( ref, GENERAL_NAME_free ); + } + } ); + + for( auto& name : sans ) { + GENERAL_NAME* gen = GENERAL_NAME_new(); + + if( !gen ) { + throw "Malloc failure."; + } + + gen->type = name->type == "DNS" ? GEN_DNS : name->type == "email" ? GEN_EMAIL : 0; // GEN_EMAIL; + + if( !gen->type + || !( gen->d.ia5 = M_ASN1_IA5STRING_new() ) + || !ASN1_STRING_set( gen->d.ia5, name->content.data(), name->content.size() ) ) { + GENERAL_NAME_free( gen ); + throw "initing iasting5 failed"; + } + + sk_GENERAL_NAME_push( gens.get(), gen ); + } + + X509_EXTENSION* ext = do_ext_i2d( NID_subject_alt_name, 0/*critical*/, ( ASN1_VALUE* )gens.get() ); + + X509_add_ext( target.get(), ext, -1 ); + X509_EXTENSION_free( ext ); } std::string X509Cert::sign( std::shared_ptr caKey ) { @@ -111,6 +178,7 @@ std::string X509Cert::sign( std::shared_ptr caKey ) { } X509_print_fp( stdout, target.get() ); + std::shared_ptr mem = std::shared_ptr( BIO_new( BIO_s_mem() ), BIO_free ); PEM_write_bio_X509( mem.get(), target.get() ); BUF_MEM* buf; diff --git a/src/X509.h b/src/X509.h index a9d60e6..6616580 100644 --- a/src/X509.h +++ b/src/X509.h @@ -1,7 +1,12 @@ #pragma once + #include +#include + #include +#include "database.h" + class X509Req { private: std::shared_ptr pk; @@ -21,8 +26,7 @@ public: void setIssuerNameFrom( std::shared_ptr ca ); void setPubkeyFrom( std::shared_ptr r ); void setSerialNumber( int num ); - void setExtensions( std::shared_ptr caCert ); + void setExtensions( std::shared_ptr caCert, std::vector>& sans ); void setTimes( long before, long after ); std::string sign( std::shared_ptr caKey ); }; - diff --git a/src/database.h b/src/database.h index 46aed6c..a80b73e 100644 --- a/src/database.h +++ b/src/database.h @@ -2,6 +2,7 @@ #include #include +#include struct Job { std::string id; @@ -10,6 +11,12 @@ struct Job { std::string from; std::string to; }; + +struct SAN { + std::string content; + std::string type; +}; + struct TBSCertificate { std::string CN; std::string subj; @@ -18,6 +25,7 @@ struct TBSCertificate { std::string csr; std::string csr_type; std::string csr_content; + std::vector> SANs; }; class JobProvider { diff --git a/src/main.cpp b/src/main.cpp index e03c361..2d42e84 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,12 +42,19 @@ int main( int argc, const char* argv[] ) { if( job->task == "sign" ) { try { std::shared_ptr cert = jp->fetchTBSCert( job ); + + if( !cert ) { + std::cout << "wasn't able to load CSR" << std::endl; + return 2; + } + std::cout << "Found a CSR at '" << cert->csr << "' signing" << std::endl; std::ifstream t( cert->csr ); cert->csr_content = std::string( std::istreambuf_iterator( t ), std::istreambuf_iterator() ); sign->sign( cert ); } catch( const char* c ) { std::cerr << c << std::endl; + return 2; } } diff --git a/src/mysql.cpp b/src/mysql.cpp index ab37553..66c2442 100644 --- a/src/mysql.cpp +++ b/src/mysql.cpp @@ -2,6 +2,8 @@ #include +#include + #include //This static variable exists to handle initializing and finalizing the MySQL driver library @@ -93,7 +95,7 @@ std::pair< int, std::shared_ptr > MySQLJobProvider::query( const std: int err = mysql_real_query( this->conn.get(), query.c_str(), query.size() ); if( err ) { - return std::make_pair( err, std::shared_ptr() ); + throw( std::string( "MySQL error: " ) + mysql_error( this->conn.get() ) ).c_str(); } auto c = conn; @@ -153,7 +155,7 @@ std::shared_ptr MySQLJobProvider::fetchJob() { return job; } -std::string MySQLJobProvider::escape_string( const std::string& target ) { +std::string MySQLJobProvider::escape_string( const std::string & target ) { if( !conn ) { throw "Not connected!"; } @@ -185,9 +187,10 @@ bool MySQLJobProvider::finishJob( std::shared_ptr job ) { std::shared_ptr MySQLJobProvider::fetchTBSCert( std::shared_ptr job ) { std::shared_ptr cert = std::shared_ptr( new TBSCertificate() ); - std::string q = "SELECT CN, subject, md, profile, csr_name, csr_type FROM certs WHERE id='" + this->escape_string( job->id ) + "'"; + std::string q = "SELECT CN, subject, md, profile, csr_name, csr_type FROM certs WHERE id='" + this->escape_string( job->target ) + "'"; int err = 0; + std::shared_ptr res; std::tie( err, res ) = query( q ); @@ -215,5 +218,28 @@ std::shared_ptr MySQLJobProvider::fetchTBSCert( std::shared_ptr< cert->csr = std::string( row[4], row[4] + l[4] ); cert->csr_type = std::string( row[5], row[5] + l[5] ); + cert->SANs = std::vector>(); + + q = "SELECT contents, type FROM subjectAlternativeNames WHERE certId='" + this->escape_string( job->target ) + "'"; + std::tie( err, res ) = query( q ); + + if( err ) { + std::cout << mysql_error( this->conn.get() ); + return std::shared_ptr(); + } + + while( ( row = mysql_fetch_row( res.get() ) ) ) { + unsigned long* l = mysql_fetch_lengths( res.get() ); + + if( !l ) { + return std::shared_ptr(); + } + + std::shared_ptr nSAN = std::shared_ptr( new SAN() ); + nSAN->content = std::string( row[0], row[0] + l[0] ); + nSAN->type = std::string( row[1], row[1] + l[1] ); + cert->SANs.push_back( nSAN ); + } + return cert; } diff --git a/src/simpleOpensslSigner.cpp b/src/simpleOpensslSigner.cpp index 89151a6..e38aeb1 100644 --- a/src/simpleOpensslSigner.cpp +++ b/src/simpleOpensslSigner.cpp @@ -14,7 +14,10 @@ std::shared_ptr SimpleOpensslSigner::lib_ref( new int( SSL_library_init() ), []( int* ref ) { - ( void ) ref; + delete ref; + + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); } ); std::shared_ptr loadX509FromFile( std::string filename ) { @@ -92,8 +95,8 @@ void SimpleOpensslSigner::sign( std::shared_ptr cert ) { c.setIssuerNameFrom( caCert ); c.setPubkeyFrom( req ); c.setSerialNumber( 4711 ); - c.setTimes( 0, 1000 * 60 * 60 * 24 * 10 ); - c.setExtensions( caCert ); + c.setTimes( 0, 60 * 60 * 24 * 10 ); + c.setExtensions( caCert, cert->SANs ); std::string output = c.sign( caKey ); -- 2.39.2