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 }