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