1 /** 2 * X.509 SIGNED Object 3 * 4 * Copyright: 5 * (C) 1999-2007 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_obj; 12 13 import botan.constants; 14 import botan.asn1.asn1_obj; 15 import botan.filters.pipe; 16 import botan.rng.rng; 17 import botan.pubkey.x509_key; 18 import botan.pubkey.pubkey; 19 import botan.asn1.oids; 20 import botan.asn1.der_enc; 21 import botan.asn1.ber_dec; 22 import botan.utils.parsing; 23 import botan.codec.pem; 24 import std.algorithm; 25 import botan.utils.types; 26 import botan.utils.types; 27 import memutils.hashmap; 28 29 version(X509): 30 31 /** 32 * This class represents abstract X.509 signed objects as 33 * in the X.500 SIGNED macro 34 */ 35 class X509Object : ASN1Object 36 { 37 public: 38 /** 39 * The underlying data that is to be or was signed 40 * Returns: data that is or was signed 41 */ 42 final const(Vector!ubyte) tbsData() const 43 { 44 return putInSequence(m_tbs_bits); 45 } 46 47 /** 48 * Returns: signature on tbsData() 49 */ 50 final ref const(Vector!ubyte) signature() const 51 { 52 return m_sig; 53 } 54 55 /** 56 * Returns: signature algorithm that was used to generate signature 57 */ 58 final const(AlgorithmIdentifier) signatureAlgorithm() const 59 { 60 return m_sig_algo; 61 } 62 63 /** 64 * Returns: hash algorithm that was used to generate signature 65 */ 66 final string hashUsedForSignature() const 67 { 68 Vector!string sig_info = botan.utils.parsing.splitter(OIDS.lookup(m_sig_algo.oid), '/'); 69 70 if (sig_info.length != 2) 71 throw new InternalError("Invalid name format found for " ~ m_sig_algo.oid.toString()); 72 73 Vector!string pad_and_hash = parseAlgorithmName(sig_info[1]); 74 75 if (pad_and_hash.length != 2) 76 throw new InternalError("Invalid name format " ~ sig_info[1]); 77 78 return pad_and_hash[1]; 79 } 80 81 82 /** 83 * Create a signed X509 object. 84 * 85 * Params: 86 * signer = the signer used to sign the object 87 * rng = the random number generator to use 88 * algo= the algorithm identifier of the signature scheme 89 * tbs_bits = the tbs bits to be signed 90 * Returns: signed X509 object 91 */ 92 static Vector!ubyte makeSigned(ALLOC)(ref PKSigner signer, 93 RandomNumberGenerator rng, 94 in AlgorithmIdentifier algo, 95 auto const ref Vector!(ubyte, ALLOC) tbs_bits) 96 { 97 return DEREncoder() 98 .startCons(ASN1Tag.SEQUENCE) 99 .rawBytes(tbs_bits) 100 .encode(algo) 101 .encode(signer.signMessage(tbs_bits, rng), ASN1Tag.BIT_STRING) 102 .endCons() 103 .getContentsUnlocked(); 104 } 105 106 /// ditto 107 static Vector!ubyte makeSigned(ALLOC)(ref PKSigner signer, 108 RandomNumberGenerator rng, 109 in AlgorithmIdentifier algo, 110 auto const ref RefCounted!(Vector!(ubyte, ALLOC), ALLOC) tbs_bits) 111 { 112 return makeSigned(signer, rng, algo, *tbs_bits); 113 } 114 115 116 /** 117 * Check the signature on this data 118 * Params: 119 * pub_key = the public key purportedly used to sign this data 120 * Returns: true if the signature is valid, otherwise false 121 */ 122 final bool checkSignature(in PublicKey pub_key) const 123 { 124 assert(pub_key); 125 try { 126 Vector!string sig_info = botan.utils.parsing.splitter(OIDS.lookup(m_sig_algo.oid), '/'); 127 128 if (sig_info.length != 2 || sig_info[0] != pub_key.algoName) 129 return false; 130 131 string padding = sig_info[1]; 132 SignatureFormat format = (pub_key.messageParts() >= 2) ? DER_SEQUENCE : IEEE_1363; 133 PKVerifier verifier = PKVerifier(pub_key, padding, format); 134 auto tbs = tbsData(); 135 auto sig = signature().dup; 136 return verifier.verifyMessage(tbs, sig); 137 } 138 catch(Exception e) 139 { 140 return false; 141 } 142 } 143 144 override void encodeInto(ref DEREncoder to) const 145 { 146 to.startCons(ASN1Tag.SEQUENCE) 147 .startCons(ASN1Tag.SEQUENCE) 148 .rawBytes(m_tbs_bits) 149 .endCons() 150 .encode(m_sig_algo) 151 .encode(m_sig, ASN1Tag.BIT_STRING) 152 .endCons(); 153 } 154 155 /* 156 * Read a BER encoded X.509 object 157 */ 158 override void decodeFrom(ref BERDecoder from) 159 { 160 //logTrace("decodeFrom X509Object"); 161 from.startCons(ASN1Tag.SEQUENCE) 162 .startCons(ASN1Tag.SEQUENCE) 163 .rawBytes(m_tbs_bits) 164 .endCons() 165 .decode(m_sig_algo) 166 .decode(m_sig, ASN1Tag.BIT_STRING) 167 .verifyEnd() 168 .endCons(); 169 } 170 171 172 /** 173 * Returns: BER encoding of this 174 */ 175 final Vector!ubyte BER_encode() const 176 { 177 static HashMap!(void*, ubyte[]) cache; 178 if ((cast(void*)this) in cache) return Vector!ubyte(cache[cast(void*)this]); 179 180 auto der = DEREncoder(); 181 encodeInto(der); 182 auto ret = der.getContentsUnlocked(); 183 if (cache.length > 50) 184 cache = HashMap!(void*, ubyte[])(); 185 cache[cast(void*)this] = ret[].dup; 186 187 return ret.move; 188 } 189 190 191 /** 192 * Returns: PEM encoding of this 193 */ 194 final string PEM_encode() const 195 { 196 return PEM.encode(BER_encode(), m_PEM_label_pref); 197 } 198 199 ~this() {} 200 protected: 201 /* 202 * Create a generic X.509 object 203 */ 204 this(DataSource stream, in string labels) 205 { 206 init(stream, labels); 207 } 208 209 /* 210 * Create a generic X.509 object 211 */ 212 this(in string file, in string labels) 213 { 214 DataSource stream = cast(DataSource)DataSourceStream(file, true); 215 init(stream, labels); 216 } 217 218 /* 219 * Create a generic X.509 object 220 */ 221 this(ALLOC)(auto const ref Vector!(ubyte, ALLOC) vec, in string labels) 222 { 223 auto stream = DataSourceMemory(vec.ptr, vec.length); 224 init(cast(DataSource)stream, labels); 225 } 226 227 /* 228 * Create a generic X.509 object 229 */ 230 this(ALLOC)(auto const ref RefCounted!(Vector!(ubyte, ALLOC), ALLOC) vec, in string labels) 231 { 232 auto stream = DataSourceMemory(vec.ptr, vec.length); 233 init(cast(DataSource)stream, labels); 234 } 235 236 /* 237 * Try to decode the actual information 238 */ 239 final void doDecode() 240 { 241 try { 242 forceDecode(); 243 } 244 catch(DecodingError e) 245 { 246 throw new DecodingError(m_PEM_label_pref ~ " decoding failed (" ~ e.msg ~ ")"); 247 } 248 catch(InvalidArgument e) 249 { 250 throw new DecodingError(m_PEM_label_pref ~ " decoding failed (" ~ e.msg ~ ")"); 251 } 252 } 253 this() { } 254 AlgorithmIdentifier m_sig_algo; 255 Vector!ubyte m_tbs_bits, m_sig; 256 257 protected: 258 abstract void forceDecode(); 259 260 private: 261 /* 262 * Read a PEM or BER X.509 object 263 */ 264 final void init(DataSource input, in string labels) 265 { 266 m_sig_algo = AlgorithmIdentifier(); 267 m_PEM_labels_allowed = botan.utils.parsing.splitter(labels, '/'); 268 if (m_PEM_labels_allowed.length < 1) 269 throw new InvalidArgument("Bad labels argument to X509Object"); 270 271 //logTrace("Initialize PEM/BER X.509 Object"); 272 m_PEM_label_pref = m_PEM_labels_allowed[0]; 273 274 try { 275 if (maybeBER(input) && !PEM.matches(input)) 276 { 277 auto dec = BERDecoder(input); 278 decodeFrom(dec); 279 } 280 else 281 { 282 string got_label; 283 auto ber = DataSourceMemory(PEM.decode(input, got_label)); 284 if (!m_PEM_labels_allowed[].canFind(got_label)) { 285 throw new DecodingError("Invalid PEM Certificate format " ~ got_label ~ ", please convert it to a PEM or BER X.509 object format. Possible labels: " ~ m_PEM_labels_allowed[].to!string); 286 } 287 auto dec = BERDecoder(cast(DataSource)ber); 288 decodeFrom(dec); 289 } 290 } 291 catch(DecodingError e) 292 { 293 throw new DecodingError(m_PEM_label_pref ~ " decoding failed: " ~ e.msg); 294 } 295 } 296 297 Vector!string m_PEM_labels_allowed; 298 string m_PEM_label_pref; 299 }