PKCS10.java revision 11658:cae3b7b19462
1/*
2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27package sun.security.pkcs10;
28
29import java.io.PrintStream;
30import java.io.IOException;
31import java.math.BigInteger;
32
33import java.security.cert.CertificateException;
34import java.security.NoSuchAlgorithmException;
35import java.security.InvalidKeyException;
36import java.security.Signature;
37import java.security.SignatureException;
38import java.security.PublicKey;
39
40import java.util.Base64;
41
42import sun.security.util.*;
43import sun.security.x509.AlgorithmId;
44import sun.security.x509.X509Key;
45import sun.security.x509.X500Name;
46
47/**
48 * A PKCS #10 certificate request is created and sent to a Certificate
49 * Authority, which then creates an X.509 certificate and returns it to
50 * the entity that requested it. A certificate request basically consists
51 * of the subject's X.500 name, public key, and optionally some attributes,
52 * signed using the corresponding private key.
53 *
54 * The ASN.1 syntax for a Certification Request is:
55 * <pre>
56 * CertificationRequest ::= SEQUENCE {
57 *    certificationRequestInfo CertificationRequestInfo,
58 *    signatureAlgorithm       SignatureAlgorithmIdentifier,
59 *    signature                Signature
60 *  }
61 *
62 * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
63 * Signature ::= BIT STRING
64 *
65 * CertificationRequestInfo ::= SEQUENCE {
66 *    version                 Version,
67 *    subject                 Name,
68 *    subjectPublicKeyInfo    SubjectPublicKeyInfo,
69 *    attributes [0] IMPLICIT Attributes
70 * }
71 * Attributes ::= SET OF Attribute
72 * </pre>
73 *
74 * @author David Brownell
75 * @author Amit Kapoor
76 * @author Hemma Prafullchandra
77 */
78public class PKCS10 {
79    /**
80     * Constructs an unsigned PKCS #10 certificate request.  Before this
81     * request may be used, it must be encoded and signed.  Then it
82     * must be retrieved in some conventional format (e.g. string).
83     *
84     * @param publicKey the public key that should be placed
85     *          into the certificate generated by the CA.
86     */
87    public PKCS10(PublicKey publicKey) {
88        subjectPublicKeyInfo = publicKey;
89        attributeSet = new PKCS10Attributes();
90    }
91
92    /**
93     * Constructs an unsigned PKCS #10 certificate request.  Before this
94     * request may be used, it must be encoded and signed.  Then it
95     * must be retrieved in some conventional format (e.g. string).
96     *
97     * @param publicKey the public key that should be placed
98     *          into the certificate generated by the CA.
99     * @param attributes additonal set of PKCS10 attributes requested
100     *          for in the certificate.
101     */
102    public PKCS10(PublicKey publicKey, PKCS10Attributes attributes) {
103        subjectPublicKeyInfo = publicKey;
104        attributeSet = attributes;
105    }
106
107    /**
108     * Parses an encoded, signed PKCS #10 certificate request, verifying
109     * the request's signature as it does so.  This constructor would
110     * typically be used by a Certificate Authority, from which a new
111     * certificate would then be constructed.
112     *
113     * @param data the DER-encoded PKCS #10 request.
114     * @exception IOException for low level errors reading the data
115     * @exception SignatureException when the signature is invalid
116     * @exception NoSuchAlgorithmException when the signature
117     *  algorithm is not supported in this environment
118     */
119    public PKCS10(byte[] data)
120    throws IOException, SignatureException, NoSuchAlgorithmException {
121        DerInputStream  in;
122        DerValue[]      seq;
123        AlgorithmId     id;
124        byte[]          sigData;
125        Signature       sig;
126
127        encoded = data;
128
129        //
130        // Outer sequence:  request, signature algorithm, signature.
131        // Parse, and prepare to verify later.
132        //
133        in = new DerInputStream(data);
134        seq = in.getSequence(3);
135
136        if (seq.length != 3)
137            throw new IllegalArgumentException("not a PKCS #10 request");
138
139        data = seq[0].toByteArray();            // reusing this variable
140        id = AlgorithmId.parse(seq[1]);
141        sigData = seq[2].getBitString();
142
143        //
144        // Inner sequence:  version, name, key, attributes
145        //
146        BigInteger      serial;
147        DerValue        val;
148
149        serial = seq[0].data.getBigInteger();
150        if (!serial.equals(BigInteger.ZERO))
151            throw new IllegalArgumentException("not PKCS #10 v1");
152
153        subject = new X500Name(seq[0].data);
154        subjectPublicKeyInfo = X509Key.parse(seq[0].data.getDerValue());
155
156        // Cope with a somewhat common illegal PKCS #10 format
157        if (seq[0].data.available() != 0)
158            attributeSet = new PKCS10Attributes(seq[0].data);
159        else
160            attributeSet = new PKCS10Attributes();
161
162        if (seq[0].data.available() != 0)
163            throw new IllegalArgumentException("illegal PKCS #10 data");
164
165        //
166        // OK, we parsed it all ... validate the signature using the
167        // key and signature algorithm we found.
168        //
169        try {
170            sig = Signature.getInstance(id.getName());
171            sig.initVerify(subjectPublicKeyInfo);
172            sig.update(data);
173            if (!sig.verify(sigData))
174                throw new SignatureException("Invalid PKCS #10 signature");
175        } catch (InvalidKeyException e) {
176            throw new SignatureException("invalid key");
177        }
178    }
179
180    /**
181     * Create the signed certificate request.  This will later be
182     * retrieved in either string or binary format.
183     *
184     * @param subject identifies the signer (by X.500 name).
185     * @param signature private key and signing algorithm to use.
186     * @exception IOException on errors.
187     * @exception CertificateException on certificate handling errors.
188     * @exception SignatureException on signature handling errors.
189     */
190    public void encodeAndSign(X500Name subject, Signature signature)
191    throws CertificateException, IOException, SignatureException {
192        DerOutputStream out, scratch;
193        byte[]          certificateRequestInfo;
194        byte[]          sig;
195
196        if (encoded != null)
197            throw new SignatureException("request is already signed");
198
199        this.subject = subject;
200
201        /*
202         * Encode cert request info, wrap in a sequence for signing
203         */
204        scratch = new DerOutputStream();
205        scratch.putInteger(BigInteger.ZERO);            // PKCS #10 v1.0
206        subject.encode(scratch);                        // X.500 name
207        scratch.write(subjectPublicKeyInfo.getEncoded()); // public key
208        attributeSet.encode(scratch);
209
210        out = new DerOutputStream();
211        out.write(DerValue.tag_Sequence, scratch);      // wrap it!
212        certificateRequestInfo = out.toByteArray();
213        scratch = out;
214
215        /*
216         * Sign it ...
217         */
218        signature.update(certificateRequestInfo, 0,
219                certificateRequestInfo.length);
220        sig = signature.sign();
221
222        /*
223         * Build guts of SIGNED macro
224         */
225        AlgorithmId algId = null;
226        try {
227            algId = AlgorithmId.get(signature.getAlgorithm());
228        } catch (NoSuchAlgorithmException nsae) {
229            throw new SignatureException(nsae);
230        }
231        algId.encode(scratch);     // sig algorithm
232        scratch.putBitString(sig);                      // sig
233
234        /*
235         * Wrap those guts in a sequence
236         */
237        out = new DerOutputStream();
238        out.write(DerValue.tag_Sequence, scratch);
239        encoded = out.toByteArray();
240    }
241
242    /**
243     * Returns the subject's name.
244     */
245    public X500Name getSubjectName() { return subject; }
246
247    /**
248     * Returns the subject's public key.
249     */
250    public PublicKey getSubjectPublicKeyInfo()
251        { return subjectPublicKeyInfo; }
252
253    /**
254     * Returns the additional attributes requested.
255     */
256    public PKCS10Attributes getAttributes()
257        { return attributeSet; }
258
259    /**
260     * Returns the encoded and signed certificate request as a
261     * DER-encoded byte array.
262     *
263     * @return the certificate request, or null if encodeAndSign()
264     *          has not yet been called.
265     */
266    public byte[] getEncoded() {
267        if (encoded != null)
268            return encoded.clone();
269        else
270            return null;
271    }
272
273    /**
274     * Prints an E-Mailable version of the certificate request on the print
275     * stream passed.  The format is a common base64 encoded one, supported
276     * by most Certificate Authorities because Netscape web servers have
277     * used this for some time.  Some certificate authorities expect some
278     * more information, in particular contact information for the web
279     * server administrator.
280     *
281     * @param out the print stream where the certificate request
282     *  will be printed.
283     * @exception IOException when an output operation failed
284     * @exception SignatureException when the certificate request was
285     *  not yet signed.
286     */
287    public void print(PrintStream out)
288    throws IOException, SignatureException {
289        if (encoded == null)
290            throw new SignatureException("Cert request was not signed");
291
292
293        byte[] CRLF = new byte[] {'\r', '\n'};
294        out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");
295        out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded));
296        out.println("-----END NEW CERTIFICATE REQUEST-----");
297    }
298
299    /**
300     * Provides a short description of this request.
301     */
302    public String toString() {
303        return "[PKCS #10 certificate request:\n"
304            + subjectPublicKeyInfo.toString()
305            + " subject: <" + subject + ">" + "\n"
306            + " attributes: " + attributeSet.toString()
307            + "\n]";
308    }
309
310    /**
311     * Compares this object for equality with the specified
312     * object. If the <code>other</code> object is an
313     * <code>instanceof</code> <code>PKCS10</code>, then
314     * its encoded form is retrieved and compared with the
315     * encoded form of this certificate request.
316     *
317     * @param other the object to test for equality with this object.
318     * @return true iff the encoded forms of the two certificate
319     * requests match, false otherwise.
320     */
321    public boolean equals(Object other) {
322        if (this == other)
323            return true;
324        if (!(other instanceof PKCS10))
325            return false;
326        if (encoded == null) // not signed yet
327            return false;
328        byte[] otherEncoded = ((PKCS10)other).getEncoded();
329        if (otherEncoded == null)
330            return false;
331
332        return java.util.Arrays.equals(encoded, otherEncoded);
333    }
334
335    /**
336     * Returns a hashcode value for this certificate request from its
337     * encoded form.
338     *
339     * @return the hashcode value.
340     */
341    public int hashCode() {
342        int     retval = 0;
343        if (encoded != null)
344            for (int i = 1; i < encoded.length; i++)
345             retval += encoded[i] * i;
346        return(retval);
347    }
348
349    private X500Name            subject;
350    private PublicKey           subjectPublicKeyInfo;
351    private PKCS10Attributes    attributeSet;
352    private byte[]              encoded;        // signed
353}
354