]> WPIA git - cassiopeia.git/blob - src/crypto/X509.cpp
add: Initial code to implement revocation
[cassiopeia.git] / src / crypto / X509.cpp
1 #include "X509.h"
2
3 #include <fstream>
4 #include <iostream>
5
6 #include <openssl/ssl.h>
7 #include <openssl/bio.h>
8 #include <openssl/x509v3.h>
9
10 X509Req::X509Req( X509_REQ* csr ) {
11     req = std::shared_ptr<X509_REQ>( csr, X509_REQ_free );
12     EVP_PKEY* pkt = X509_REQ_get_pubkey( req.get() );
13
14     if( !pkt ) {
15         throw "Error extracting public key";
16     }
17
18     pk = std::shared_ptr<EVP_PKEY>( pkt, EVP_PKEY_free );
19 }
20
21 X509Req::X509Req( std::string spkac ) {
22     if( spkac.compare( 0, 6, "SPKAC=" ) != 0 ) {
23         throw "Error: not a SPKAC";
24     }
25
26     spkac = spkac.substr( 6 );
27     NETSCAPE_SPKI* spki_p = NETSCAPE_SPKI_b64_decode( spkac.c_str(), spkac.size() );
28
29     if( !spki_p ) {
30         throw "Error: decode failed";
31     }
32
33     spki = std::shared_ptr<NETSCAPE_SPKI>( spki_p, NETSCAPE_SPKI_free );
34     EVP_PKEY* pkt_p = NETSCAPE_SPKI_get_pubkey( spki.get() );
35
36     if( !pkt_p ) {
37         throw "Error: reading SPKAC Pubkey failed";
38     }
39
40     pk = std::shared_ptr<EVP_PKEY>( pkt_p, EVP_PKEY_free );
41 }
42
43 int X509Req::verify() {
44     if( !req ) {
45         return NETSCAPE_SPKI_verify( spki.get(), pk.get() );
46     }
47
48     return X509_REQ_verify( req.get(), pk.get() );
49 }
50
51 std::shared_ptr<EVP_PKEY> X509Req::getPkey() {
52     return pk;
53 }
54
55 std::shared_ptr<X509Req> X509Req::parseCSR( std::string content ) {
56     std::shared_ptr<BIO> in = std::shared_ptr<BIO>( BIO_new_mem_buf( const_cast<char*>( content.c_str() ), -1 ), BIO_free );
57     X509_REQ* req = PEM_read_bio_X509_REQ( in.get(), NULL, NULL, NULL );
58
59     if( !req ) {
60         throw "Error parsing CSR";
61     }
62
63     return std::shared_ptr<X509Req>( new X509Req( req ) );
64 }
65
66 std::shared_ptr<X509Req> X509Req::parseSPKAC( std::string content ) {
67     return std::shared_ptr<X509Req>( new X509Req( content ) );
68 }
69
70 int add_ext( std::shared_ptr<X509> issuer, std::shared_ptr<X509> subj, int nid, const char* value ) {
71     X509_EXTENSION* ex;
72     X509V3_CTX ctx;
73
74     /* This sets the 'context' of the extensions. */
75     /* No configuration database */
76     X509V3_set_ctx_nodb( &ctx );
77
78     /* Issuer and subject certs: both the target since it is self signed,
79      * no request and no CRL
80      */
81     X509V3_set_ctx( &ctx, issuer.get(), subj.get(), NULL, NULL, 0 );
82     ex = X509V3_EXT_conf_nid( NULL, &ctx, nid, const_cast<char*>( value ) );
83
84     if( !ex ) {
85         return 0;
86     }
87
88     X509_add_ext( subj.get(), ex, -1 );
89     X509_EXTENSION_free( ex );
90
91     return 1;
92 }
93
94 X509Cert::X509Cert() {
95     X509* c = X509_new();
96
97     if( !c ) {
98         throw "malloc failed";
99     }
100
101     target = std::shared_ptr<X509>( c, X509_free );
102
103     if( !X509_set_version( c, 2 ) ) {
104         throw "Setting X509-version to 3 failed";
105     }
106
107     X509_NAME* subjectP = X509_NAME_new();
108
109     if( !subjectP ) {
110         throw "malloc failure";
111     }
112
113     subject = std::shared_ptr<X509_NAME>( subjectP, X509_NAME_free );
114 }
115
116 void X509Cert::addRDN( int nid, std::string data ) {
117     if( ! X509_NAME_add_entry_by_NID( subject.get(), nid, MBSTRING_UTF8, ( unsigned char* )const_cast<char*>( data.data() ), data.size(), -1, 0 ) ) {
118         throw "malloc failure";
119     }
120 }
121
122 void X509Cert::setIssuerNameFrom( std::shared_ptr<X509> caCert ) {
123     if( !X509_set_issuer_name( target.get(), X509_get_subject_name( caCert.get() ) ) ) {
124         throw "Error setting Issuer name";
125     }
126 }
127
128 void X509Cert::setPubkeyFrom( std::shared_ptr<X509Req> req ) {
129     std::shared_ptr<EVP_PKEY> pktmp = req->getPkey();
130
131     if( !X509_set_pubkey( target.get(), pktmp.get() ) ) {
132         throw "Setting public key failed.";
133     }
134 }
135
136 void X509Cert::setSerialNumber( BIGNUM* num ) {
137     BN_to_ASN1_INTEGER( num , target->cert_info->serialNumber );
138 }
139
140 void X509Cert::setTimes( uint32_t before, uint32_t after ) {
141     X509_gmtime_adj( X509_get_notBefore( target.get() ), before );
142     X509_gmtime_adj( X509_get_notAfter( target.get() ), after );
143 }
144
145 static X509_EXTENSION* do_ext_i2d( int ext_nid, int crit, ASN1_VALUE* ext_struc ) {
146     unsigned char* ext_der;
147     int ext_len;
148     ASN1_OCTET_STRING* ext_oct;
149     X509_EXTENSION* ext;
150     /* Convert internal representation to DER */
151     ext_der = NULL;
152     ext_len = ASN1_item_i2d( ext_struc, &ext_der, ASN1_ITEM_ptr( ASN1_ITEM_ref( GENERAL_NAMES ) ) );
153
154     if( ext_len < 0 ) {
155         goto merr;
156     }
157
158     if( !( ext_oct = M_ASN1_OCTET_STRING_new() ) ) {
159         goto merr;
160     }
161
162     ext_oct->data = ext_der;
163     ext_oct->length = ext_len;
164
165     ext = X509_EXTENSION_create_by_NID( NULL, ext_nid, crit, ext_oct );
166
167     if( !ext ) {
168         goto merr;
169     }
170
171     M_ASN1_OCTET_STRING_free( ext_oct );
172     return ext;
173
174 merr:
175     throw "memerr";
176 }
177
178 void X509Cert::setExtensions( std::shared_ptr<X509> caCert, std::vector<std::shared_ptr<SAN>>& sans, Profile& prof ) {
179     add_ext( caCert, target, NID_basic_constraints, "critical,CA:FALSE" );
180     add_ext( caCert, target, NID_subject_key_identifier, "hash" );
181     add_ext( caCert, target, NID_authority_key_identifier, "keyid,issuer:always" );
182     std::string ku = std::string( "critical," ) + prof.ku;
183     add_ext( caCert, target, NID_key_usage, ku.c_str() );
184     add_ext( caCert, target, NID_ext_key_usage, prof.eku.c_str() );
185     add_ext( caCert, target, NID_info_access, "OCSP;URI:http://ocsp.cacert.org" );
186     add_ext( caCert, target, NID_crl_distribution_points, "URI:http://crl.cacert.org/class3-revoke.crl" );
187
188     if( sans.size() == 0 ) {
189         return;
190     }
191
192     std::shared_ptr<GENERAL_NAMES> gens = std::shared_ptr<GENERAL_NAMES>(
193         sk_GENERAL_NAME_new_null(),
194         []( GENERAL_NAMES * ref ) {
195             if( ref ) {
196                 sk_GENERAL_NAME_pop_free( ref, GENERAL_NAME_free );
197             }
198         } );
199
200     for( auto& name : sans ) {
201         GENERAL_NAME* gen = GENERAL_NAME_new();
202
203         if( !gen ) {
204             throw "Malloc failure.";
205         }
206
207         gen->type = name->type == "DNS" ? GEN_DNS : name->type == "email" ? GEN_EMAIL : 0; // GEN_EMAIL;
208
209         if( !gen->type
210                 || !( gen->d.ia5 = M_ASN1_IA5STRING_new() )
211                 || !ASN1_STRING_set( gen->d.ia5, name->content.data(), name->content.size() ) ) {
212             GENERAL_NAME_free( gen );
213             throw "initing iasting5 failed";
214         }
215
216         sk_GENERAL_NAME_push( gens.get(), gen );
217     }
218
219     X509_EXTENSION* ext = do_ext_i2d( NID_subject_alt_name, 0/*critical*/, ( ASN1_VALUE* )gens.get() );
220
221     X509_add_ext( target.get(), ext, -1 );
222     X509_EXTENSION_free( ext );
223 }
224
225 std::shared_ptr<SignedCertificate> X509Cert::sign( std::shared_ptr<EVP_PKEY> caKey, std::string signAlg ) {
226     if( !X509_set_subject_name( target.get(), subject.get() ) ) {
227         throw "error setting subject";
228     }
229
230     const EVP_MD* md;
231
232     if( signAlg == "sha512" ) {
233         md = EVP_sha512();
234     } else if( signAlg == "sha384" ) {
235         md = EVP_sha384();
236     } else if( signAlg == "sha256" ) {
237         md = EVP_sha256();
238     } else if( signAlg == "sha1" ) {
239         md = EVP_sha1();
240     } else {
241         throw "Unknown md-type";
242     }
243
244     if( !X509_sign( target.get(), caKey.get(), md ) ) {
245         throw "Signing failed.";
246     }
247
248     //X509_print_fp( stdout, target.get() );
249
250     std::shared_ptr<BIO> mem = std::shared_ptr<BIO>( BIO_new( BIO_s_mem() ), BIO_free );
251     PEM_write_bio_X509( mem.get(), target.get() );
252     BUF_MEM* buf;
253     BIO_get_mem_ptr( mem.get(), &buf );
254     std::shared_ptr<SignedCertificate> res = std::shared_ptr<SignedCertificate>( new SignedCertificate() );
255     res->certificate = std::string( buf->data, buf->data + buf->length );
256     BIGNUM* ser = ASN1_INTEGER_to_BN( target->cert_info->serialNumber, NULL );
257     char* serStr = BN_bn2hex( ser );
258     res->serial = std::string( serStr );
259     OPENSSL_free( serStr );
260     BN_free( ser );
261     return res;
262 }