1 /**
2 * PKCS #10
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.pkcs10;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_X509_CERTIFICATES):
15 
16 import botan.cert.x509.x509_obj;
17 import botan.asn1.x509_dn;
18 import botan.pubkey.pkcs8;
19 import botan.utils.datastor.datastor;
20 import botan.cert.x509.key_constraint;
21 import botan.asn1.asn1_attribute;
22 import botan.asn1.asn1_alt_name;
23 import botan.cert.x509.pkcs10;
24 import botan.cert.x509.x509_ext;
25 import botan.cert.x509.x509cert;
26 import botan.asn1.der_enc;
27 import botan.asn1.ber_dec;
28 import botan.utils.parsing;
29 import botan.asn1.oids;
30 import botan.codec.pem;
31 import botan.utils.types;
32 import botan.utils.exceptn;
33 import botan.utils.mem_ops;
34 import memutils.utils;
35 
36 alias PKCS10Request = RefCounted!PKCS10RequestImpl;
37 
38 /**
39 * PKCS #10 Certificate Request.
40 */
41 final class PKCS10RequestImpl : X509Object
42 {
43 public:
44     /**
45     * Get the subject public key.
46     * Returns: subject public key
47     */
48     PublicKey subjectPublicKey() const
49     {
50         auto source = DataSourceMemory(m_info.get1("X509.Certificate.public_key"));
51         return x509_key.loadKey(cast(DataSource)source);
52     }
53 
54 
55     /**
56     * Get the raw DER encoded public key.
57     * Returns: the public key of the requestor
58     */
59     Vector!ubyte rawPublicKey() const
60     {
61         auto source = DataSourceMemory(m_info.get1("X509.Certificate.public_key"));
62         return unlock(PEM.decodeCheckLabel(cast(DataSource)source, "PUBLIC KEY"));
63     }
64 
65     /**
66     * Get the subject DN.
67     * Returns: the name of the requestor
68     */
69     X509DN subjectDn() const
70     {
71         return createDn(m_info);
72     }
73 
74     /**
75     * Get the subject alternative name.
76     * Returns: the alternative names of the requestor
77     */
78     AlternativeName subjectAltName() const
79     {
80         return createAltName(m_info);
81     }
82 
83     /**
84     * Get the key constraints for the key associated with this
85     * PKCS#10 object.
86     * Returns: the key constraints (if any)
87     */
88     KeyConstraints constraints() const
89     {
90         return cast(KeyConstraints)m_info.get1Uint("X509v3.KeyUsage", KeyConstraints.NO_CONSTRAINTS);
91     }
92 
93     /**
94     * Get the extendend key constraints (if any).
95     * Returns: the extendend key constraints (if any)
96     */
97     Vector!OID exConstraints() const
98     {
99         Vector!string oids = m_info.get("X509v3.ExtendedKeyUsage");
100         
101         Vector!OID result;
102         foreach (oid; oids[])
103             result.pushBack(OID(oid));
104         return result;
105     }
106 
107     /**
108     * Find out whether this is a CA request.
109     * Returns: true if it is a CA request, false otherwise.
110     */
111     bool isCA() const
112     {
113         return (m_info.get1Uint("X509v3.BasicConstraints.is_ca") > 0);
114     }
115 
116 
117     /**
118     * Return the constraint on the path length defined
119     * in the BasicConstraints extension.
120     * Returns: the desired path limit (if any)
121     */
122     uint pathLimit() const
123     {
124         return m_info.get1Uint("X509v3.BasicConstraints.path_constraint", 0);
125     }
126 
127     /**
128     * Get the challenge password for this request
129     * Returns: challenge password for this request
130     */
131     string challengePassword() const
132     {
133         return m_info.get1("PKCS9.ChallengePassword");
134     }
135 
136     /**
137     * Create a PKCS#10 Request from a data source.
138     *
139     * Params:
140     *  source = the data source providing the DER encoded request
141     */
142     this(DataSource source)
143     {
144         super(source, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST");
145         doDecode();
146     }
147 
148     /**
149     * Create a PKCS#10 Request from a file.
150     *
151     * Params:
152     *  input = the name of the file containing the DER or PEM encoded request file
153     */
154     this(in string input)
155     {
156         super(input, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST");
157         doDecode();
158     }
159 
160     /**
161     * Create a PKCS#10 Request from binary data.
162     *
163     * Params:
164     *  input = a $(D Vector) containing the DER value
165     */
166     this(ALLOC)(in Vector!(ubyte, ALLOC)* input)
167     {
168         super(*input, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST");
169         doDecode();
170     }
171 protected:
172     /*
173     * Deocde the CertificateRequestInfo
174     */
175     override void forceDecode()
176     {
177         //logTrace("ForceDecode PKCS10Request");
178         BERDecoder cert_req_info = BERDecoder(m_tbs_bits);
179         
180         size_t _version;
181         cert_req_info.decode(_version);
182         if (_version != 0)
183             throw new DecodingError("Unknown version code in PKCS #10 request: " ~ to!string(_version));
184         
185         X509DN dn_subject = X509DN();
186         cert_req_info.decode(dn_subject);
187         
188         m_info.add(dn_subject.contents());
189         
190         BERObject public_key = cert_req_info.getNextObject();
191         if (public_key.type_tag != ASN1Tag.SEQUENCE || public_key.class_tag != ASN1Tag.CONSTRUCTED)
192             throw new BERBadTag("PKCS10Request: Unexpected tag for public key",
193                                   public_key.type_tag, public_key.class_tag);
194         
195         m_info.add("X509.Certificate.public_key", 
196                    PEM.encode(putInSequence(unlock(public_key.value)), "PUBLIC KEY"));
197         
198         BERObject attr_bits = cert_req_info.getNextObject();
199         
200         if (attr_bits.type_tag == 0 &&
201             attr_bits.class_tag == (ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC))
202         {
203             auto attributes = BERDecoder(attr_bits.value);
204             while (attributes.moreItems())
205             {
206                 auto attr = Attribute();
207                 attributes.decode(attr);
208                 handleAttribute(attr);
209             }
210             attributes.verifyEnd();
211         }
212         else if (attr_bits.type_tag != ASN1Tag.NO_OBJECT)
213             throw new BERBadTag("PKCS10Request: Unexpected tag for attributes",
214                                   attr_bits.type_tag, attr_bits.class_tag);
215         
216         cert_req_info.verifyEnd();
217 		Unique!PublicKey pubkey = subjectPublicKey();
218 		if (!this.checkSignature(*pubkey))
219             throw new DecodingError("PKCS #10 request: Bad signature detected");
220     }
221 
222     /*
223     * Handle attributes in a PKCS #10 request
224     */
225     void handleAttribute(in Attribute attr)
226     {
227         auto value = BERDecoder(attr.parameters);
228         
229         if (attr.oid == OIDS.lookup("PKCS9.EmailAddress"))
230         {
231             ASN1String email = ASN1String("");
232             value.decode(email);
233             m_info.add("RFC822", email.value());
234         }
235         else if (attr.oid == OIDS.lookup("PKCS9.ChallengePassword"))
236         {
237             ASN1String challenge_password = ASN1String("");
238             value.decode(challenge_password);
239             m_info.add("PKCS9.ChallengePassword", challenge_password.value());
240         }
241         else if (attr.oid == OIDS.lookup("PKCS9.ExtensionRequest"))
242         {
243             X509Extensions extensions = X509Extensions(true);
244             value.decode(extensions).verifyEnd();
245             
246             DataStore issuer_info;
247             extensions.contentsTo(m_info, issuer_info);
248         }
249     }
250 
251 
252     DataStore m_info;
253 }