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