1 /**
2 * X.509 Certificate Authority
3 * 
4 * Copyright:
5 * (C) 1999-2008 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.cert.x509.x509_ca;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_X509_CERTIFICATES):
15 
16 public import botan.cert.x509.crl_ent;
17 import botan.asn1.asn1_time;
18 import botan.cert.x509.x509cert;
19 import botan.cert.x509.x509_crl;
20 import botan.cert.x509.x509_ext;
21 import botan.pubkey.pkcs8;
22 import botan.pubkey.pubkey;
23 import botan.cert.x509.pkcs10;
24 import botan.asn1.der_enc;
25 import botan.asn1.ber_dec;
26 import botan.math.bigint.bigint;
27 import botan.utils.parsing;
28 import botan.libstate.lookup;
29 import botan.asn1.oids;
30 import botan.cert.x509.key_constraint;
31 import botan.rng.rng;
32 import std.datetime;
33 import std.algorithm;
34 import botan.utils.mem_ops;
35 import memutils.utils;
36 
37 alias X509CA = RefCounted!X509CAImpl;
38 
39 /**
40 * This class represents X.509 Certificate Authorities (CAs).
41 */
42 final class X509CAImpl
43 {
44 public:
45     /**
46     * Sign a PKCS#10 Request.
47     *
48     * Params:
49     *  req = the request to sign
50     *  rng = the rng to use
51     *  not_before = the starting time for the certificate
52     *  not_after = the expiration time for the certificate
53     * Returns: resulting certificate
54     */
55     X509Certificate signRequest(in PKCS10Request req,
56                                   RandomNumberGenerator rng,
57                                   in X509Time not_before,
58                                   in X509Time not_after)
59     {
60         KeyConstraints constraints;
61         if (req.isCA())
62             constraints = KeyConstraints.KEY_CERT_SIGN | KeyConstraints.CRL_SIGN;
63         else
64         {
65             Unique!PublicKey key = req.subjectPublicKey();
66             constraints = findConstraints(*key, req.constraints());
67         }
68 
69         X509Extensions extensions;
70         
71         extensions.add(new BasicConstraints(req.isCA(), req.pathLimit()), true);
72         
73         extensions.add(new KeyUsage(constraints), true);
74         
75         extensions.add(new AuthorityKeyID(m_cert.subjectKeyId().dup));
76         extensions.add(new SubjectKeyID(req.rawPublicKey().dup));
77         
78         extensions.add(new SubjectAlternativeName(req.subjectAltName()));
79         
80         extensions.add(new ExtendedKeyUsage(req.exConstraints()));
81 
82         return makeCert(m_signer, rng, m_ca_sig_algo,
83                          req.rawPublicKey(),
84                          not_before, not_after,
85                          m_cert.subjectDn(), req.subjectDn(),
86                          extensions);
87     }
88 
89     /**
90     * Get the certificate of this CA.
91     * Returns: CA certificate
92     */
93     const(X509Certificate) caCertificate() const
94     {
95         return m_cert;
96     }
97 
98     /**
99     * Create a new and empty CRL for this CA.
100     *
101     * Params:
102     *  rng = the random number generator to use
103     *  next_update = the time to set in next update in seconds
104     * as the offset from the current time
105     * Returns: new CRL
106     */
107     X509CRL newCRL(RandomNumberGenerator rng, Duration next_update = 0.seconds) const
108     {
109         Vector!CRLEntry empty;
110         return makeCRL(empty, 1, next_update, rng);
111     }
112 
113     /**
114     * Create a new CRL by with additional entries.
115     *
116     * Params:
117     *  crl = the last CRL of this CA to add the new entries to
118     *  new_revoked = contains the new CRL entries to be added to the CRL
119     *  rng = the random number generator to use
120     *  next_update = the time to set in next update in $(D Duration) from now
121     */
122     X509CRL updateCRL()(in X509CRL crl,
123                         auto const ref Vector!CRLEntry new_revoked,
124                         RandomNumberGenerator rng,
125                         Duration next_update = 0.seconds) const
126     {
127 
128         Vector!CRLEntry revoked = crl.getRevoked().dup;
129         revoked ~= new_revoked[];
130         return makeCRL(revoked, crl.crlNumber() + 1, next_update, rng);
131     }
132 
133 
134     /**
135     * Interface for creating new certificates
136     * Params:
137     *  signer = a signing object
138     *  rng = a random number generator
139     *  sig_algo = the signature algorithm identifier
140     *  pub_key = the serialized public key
141     *  not_before = the start time of the certificate
142     *  not_after = the end time of the certificate
143     *  issuer_dn = the DN of the issuer
144     *  subject_dn = the DN of the subject
145     *  extensions = an optional list of certificate extensions
146     * Returns:s newly minted certificate
147     */
148     static X509Certificate makeCert(ALLOC)(ref PKSigner signer,
149                                            RandomNumberGenerator rng,
150                                            in AlgorithmIdentifier sig_algo,
151                                            auto const ref Vector!(ubyte, ALLOC) pub_key,
152                                            in X509Time not_before,
153                                            in X509Time not_after,
154                                            in X509DN issuer_dn,
155                                            in X509DN subject_dn,
156                                            in X509Extensions extensions)
157     {
158         __gshared immutable size_t X509_CERT_VERSION = 3;
159         __gshared immutable size_t SERIAL_BITS = 128;
160         
161         BigInt serial_no = BigInt(rng, SERIAL_BITS);
162         auto contents =
163             DEREncoder().startCons(ASN1Tag.SEQUENCE)
164                 .startExplicit(0)
165                 .encode(X509_CERT_VERSION-1)
166                 .endExplicit()
167                 
168                 .encode(serial_no)
169                 
170                 .encode(sig_algo)
171                 .encode(issuer_dn)
172                 
173                 .startCons(ASN1Tag.SEQUENCE)
174                 .encode(not_before)
175                 .encode(not_after)
176                 .endCons()
177                 
178                 .encode(subject_dn)
179                 .rawBytes(pub_key)
180                 
181                 .startExplicit(3)
182                 .startCons(ASN1Tag.SEQUENCE)
183                 .encode(extensions)
184                 .endCons()
185                 .endExplicit()
186                 .endCons()
187                 .getContents();
188         Vector!ubyte cert = X509Object.makeSigned(signer, rng, sig_algo, contents.move);
189         
190         return X509Certificate(cert.move);
191     }
192 
193     /**
194     * Create a new CA object. Load the certificate and private key
195     * Params:
196     *  c = the certificate of the CA
197     *  key = the private key of the CA
198     *  hash_fn = name of a hash function to use for signing
199     */
200     this(X509Certificate c,
201          in PrivateKey key,
202          in string hash_fn)
203     {
204         m_ca_sig_algo = AlgorithmIdentifier();
205         m_cert = c;
206         if (!m_cert.isCACert())
207             throw new InvalidArgument("X509_CA: This certificate is not for a CA");
208         
209         m_signer = chooseSigFormat(key, hash_fn, m_ca_sig_algo);
210     }
211 
212     /*
213     * X509_CA Destructor
214     */
215     ~this()
216     {
217     }
218 private:
219     /*
220     * Create a CRL
221     */
222     X509CRL makeCRL(const ref Vector!CRLEntry revoked,
223                     uint crl_number, Duration next_update,
224                     RandomNumberGenerator rng) const
225     {
226         __gshared immutable size_t X509_CRL_VERSION = 2;
227         
228         if (next_update == 0.seconds)
229             next_update = 7.days;
230         
231         // Totally stupid: ties encoding logic to the return of std::time!!
232         auto current_time = Clock.currTime(UTC());
233         auto expire_time = current_time + next_update;
234         
235         X509Extensions extensions;
236         extensions.add(new AuthorityKeyID(m_cert.subjectKeyId().dup));
237         extensions.add(new CRLNumber(crl_number));
238 
239         auto contents = 
240             DEREncoder().startCons(ASN1Tag.SEQUENCE)
241                 .encode(X509_CRL_VERSION-1)
242                 .encode(m_ca_sig_algo)
243                 .encode(m_cert.issuerDn())
244                 .encode(X509Time(current_time))
245                 .encode(X509Time(expire_time))
246                 .encodeIf (revoked.length > 0,
247                     DEREncoder()
248                     .startCons(ASN1Tag.SEQUENCE)
249                     .encodeList(revoked)
250                     .endCons()
251                     )
252                 .startExplicit(0)
253                 .startCons(ASN1Tag.SEQUENCE)
254                 .encode(extensions)
255                 .endCons()
256                 .endExplicit()
257                 .endCons()
258                 .getContents();
259 
260         Vector!ubyte crl = X509Object.makeSigned(*cast(PKSigner*)&m_signer, rng, m_ca_sig_algo, contents);
261         
262         return X509CRL(crl);
263     }    
264 
265 
266     AlgorithmIdentifier m_ca_sig_algo;
267     X509Certificate m_cert;
268     PKSigner m_signer;
269 }
270 
271 /**
272 * Choose the default signature format for a certain public key signature
273 * scheme.
274 * Params:
275 *  key = will be the key to choose a padding scheme for
276 *  hash_fn = is the desired hash function
277 *  sig_algo = will be set to the chosen scheme
278 * Returns: A PKSigner object for generating signatures
279 */
280 PKSigner chooseSigFormat(in PrivateKey key,
281                          in string hash_fn,
282                          ref AlgorithmIdentifier sig_algo)
283 {
284     import std.array : Appender;
285     Appender!string padding;
286     
287     const string algo_name = key.algoName;
288     
289     const HashFunction proto_hash = retrieveHash(hash_fn);
290     if (!proto_hash)
291         throw new AlgorithmNotFound(hash_fn);
292     
293     if (key.maxInputBits() < proto_hash.outputLength*8)
294         throw new InvalidArgument("Key is too small for chosen hash function");
295     
296     if (algo_name == "RSA")
297         padding ~= "EMSA3";
298     else if (algo_name == "DSA")
299         padding ~= "EMSA1";
300     else if (algo_name == "ECDSA")
301         padding ~= "EMSA1_BSI";
302     else
303         throw new InvalidArgument("Unknown X.509 signing key type: " ~ algo_name);
304     
305     SignatureFormat format = (key.messageParts() > 1) ? DER_SEQUENCE : IEEE_1363;
306 
307     padding ~= '(' ~ proto_hash.name ~ ')';
308     
309     sig_algo.oid = OIDS.lookup(algo_name ~ "/" ~ padding.data);
310 	auto algo_id = key.algorithmIdentifier();
311     sig_algo.parameters = algo_id.parameters;
312     //logTrace("chooseSigFormat Sig algo: ", sig_algo.toString());
313     return PKSigner(key, padding.data, format);
314 }