]> WPIA git - cassiopeia.git/blob - src/X509.cpp
add: Aim for initial interoperatibility with Gigi
[cassiopeia.git] / src / X509.cpp
1 #include "X509.h"
2
3 #include <iostream>
4
5 #include <openssl/ssl.h>
6 #include <openssl/bio.h>
7 #include <openssl/x509v3.h>
8
9 X509Req::X509Req( X509_REQ* csr ) {
10     req = std::shared_ptr<X509_REQ>( csr, X509_REQ_free );
11     EVP_PKEY* pkt = X509_REQ_get_pubkey( req.get() );
12
13     if( !pkt ) {
14         throw "Error extracting public key";
15     }
16
17     pk = std::shared_ptr<EVP_PKEY>( pkt, EVP_PKEY_free );
18 }
19
20 int X509Req::verify() {
21     return X509_REQ_verify( req.get(), pk.get() );
22 }
23
24 std::shared_ptr<EVP_PKEY> X509Req::getPkey() {
25     return pk;
26 }
27
28 std::shared_ptr<X509Req> X509Req::parse( std::string filename ) {
29     std::shared_ptr<BIO> in = std::shared_ptr<BIO>( BIO_new_mem_buf( const_cast<char*>( filename.c_str() ), -1 ), BIO_free );
30     X509_REQ* req = PEM_read_bio_X509_REQ( in.get(), NULL, NULL, NULL );
31
32     if( !req ) {
33         throw "Error parsing CSR";
34     }
35
36     return std::shared_ptr<X509Req>( new X509Req( req ) );
37 }
38
39 int add_ext( std::shared_ptr<X509> issuer, std::shared_ptr<X509> subj, int nid, const char* value ) {
40     X509_EXTENSION* ex;
41     X509V3_CTX ctx;
42
43     /* This sets the 'context' of the extensions. */
44     /* No configuration database */
45     X509V3_set_ctx_nodb( &ctx );
46
47     /* Issuer and subject certs: both the target since it is self signed,
48      * no request and no CRL
49      */
50     X509V3_set_ctx( &ctx, issuer.get(), subj.get(), NULL, NULL, 0 );
51     ex = X509V3_EXT_conf_nid( NULL, &ctx, nid, const_cast<char*>( value ) );
52
53     if( !ex ) {
54         return 0;
55     }
56
57     X509_add_ext( subj.get(), ex, -1 );
58     X509_EXTENSION_free( ex );
59
60     return 1;
61 }
62
63 X509Cert::X509Cert() {
64     X509* c = X509_new();
65
66     if( !c ) {
67         throw "malloc failed";
68     }
69
70     target = std::shared_ptr<X509>( c, X509_free );
71
72     if( !X509_set_version( c, 2 ) ) {
73         throw "Setting X509-version to 3 failed";
74     }
75 }
76
77 void X509Cert::setIssuerNameFrom( std::shared_ptr<X509> caCert ) {
78     if( !X509_set_issuer_name( target.get(), X509_get_subject_name( caCert.get() ) ) ) {
79         throw "Error setting Issuer name";
80     }
81 }
82
83 void X509Cert::setPubkeyFrom( std::shared_ptr<X509Req> req ) {
84     std::shared_ptr<EVP_PKEY> pktmp = req->getPkey();
85
86     if( !X509_set_pubkey( target.get(), pktmp.get() ) ) {
87         throw "Setting public key failed.";
88     }
89 }
90
91 void X509Cert::setSerialNumber( int num ) {
92     ASN1_INTEGER_set( target.get()->cert_info->serialNumber, num );
93 }
94
95 void X509Cert::setTimes( uint32_t before, uint32_t after ) {
96     X509_gmtime_adj( X509_get_notBefore( target.get() ), before );
97     X509_gmtime_adj( X509_get_notAfter( target.get() ), after );
98 }
99
100 static X509_EXTENSION* do_ext_i2d( int ext_nid, int crit, ASN1_VALUE* ext_struc ) {
101     unsigned char* ext_der;
102     int ext_len;
103     ASN1_OCTET_STRING* ext_oct;
104     X509_EXTENSION* ext;
105     /* Convert internal representation to DER */
106     ext_der = NULL;
107     ext_len = ASN1_item_i2d( ext_struc, &ext_der, ASN1_ITEM_ptr( ASN1_ITEM_ref( GENERAL_NAMES ) ) );
108
109     if( ext_len < 0 ) {
110         goto merr;
111     }
112
113     if( !( ext_oct = M_ASN1_OCTET_STRING_new() ) ) {
114         goto merr;
115     }
116
117     ext_oct->data = ext_der;
118     ext_oct->length = ext_len;
119
120     ext = X509_EXTENSION_create_by_NID( NULL, ext_nid, crit, ext_oct );
121
122     if( !ext ) {
123         goto merr;
124     }
125
126     M_ASN1_OCTET_STRING_free( ext_oct );
127     return ext;
128
129 merr:
130     throw "memerr";
131 }
132
133 void X509Cert::setExtensions( std::shared_ptr<X509> caCert, std::vector<std::shared_ptr<SAN>>& sans ) {
134     add_ext( caCert, target, NID_basic_constraints, "critical,CA:FALSE" );
135     add_ext( caCert, target, NID_subject_key_identifier, "hash" );
136     add_ext( caCert, target, NID_authority_key_identifier, "keyid,issuer:always" );
137     add_ext( caCert, target, NID_key_usage, "critical,nonRepudiation,digitalSignature,keyEncipherment" );
138     add_ext( caCert, target, NID_ext_key_usage, "clientAuth, serverAuth" );
139     add_ext( caCert, target, NID_info_access, "OCSP;URI:http://ocsp.cacert.org" );
140     add_ext( caCert, target, NID_crl_distribution_points, "URI:http://crl.cacert.org/class3-revoke.crl" );
141
142     std::shared_ptr<GENERAL_NAMES> gens = std::shared_ptr<GENERAL_NAMES>(
143         sk_GENERAL_NAME_new_null(),
144         []( GENERAL_NAMES * ref ) {
145             if( ref ) {
146                 sk_GENERAL_NAME_pop_free( ref, GENERAL_NAME_free );
147             }
148         } );
149
150     for( auto& name : sans ) {
151         GENERAL_NAME* gen = GENERAL_NAME_new();
152
153         if( !gen ) {
154             throw "Malloc failure.";
155         }
156
157         gen->type = name->type == "DNS" ? GEN_DNS : name->type == "email" ? GEN_EMAIL : 0; // GEN_EMAIL;
158
159         if( !gen->type
160                 || !( gen->d.ia5 = M_ASN1_IA5STRING_new() )
161                 || !ASN1_STRING_set( gen->d.ia5, name->content.data(), name->content.size() ) ) {
162             GENERAL_NAME_free( gen );
163             throw "initing iasting5 failed";
164         }
165
166         sk_GENERAL_NAME_push( gens.get(), gen );
167     }
168
169     X509_EXTENSION* ext = do_ext_i2d( NID_subject_alt_name, 0/*critical*/, ( ASN1_VALUE* )gens.get() );
170
171     X509_add_ext( target.get(), ext, -1 );
172     X509_EXTENSION_free( ext );
173 }
174
175 std::shared_ptr<SignedCertificate> X509Cert::sign( std::shared_ptr<EVP_PKEY> caKey ) {
176     if( !X509_sign( target.get(), caKey.get(), EVP_sha512() ) ) {
177         throw "Signing failed.";
178     }
179
180     //X509_print_fp( stdout, target.get() );
181
182     std::shared_ptr<BIO> mem = std::shared_ptr<BIO>( BIO_new( BIO_s_mem() ), BIO_free );
183     PEM_write_bio_X509( mem.get(), target.get() );
184     BUF_MEM* buf;
185     BIO_get_mem_ptr( mem.get(), &buf );
186     std::shared_ptr<SignedCertificate> res = std::shared_ptr<SignedCertificate>( new SignedCertificate() );
187     res->certificate = std::string( buf->data, buf->data + buf->length );
188     res->serial = ASN1_INTEGER_get( target.get()->cert_info->serialNumber );
189     return res;
190 }