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