001: /*
002: * Copyright 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.pkcs;
027:
028: import java.io.ByteArrayOutputStream;
029: import java.io.PrintStream;
030: import java.io.IOException;
031: import java.math.BigInteger;
032:
033: import java.security.cert.CertificateException;
034: import java.security.NoSuchAlgorithmException;
035: import java.security.InvalidKeyException;
036: import java.security.Signature;
037: import java.security.SignatureException;
038: import java.security.PublicKey;
039:
040: import sun.misc.BASE64Encoder;
041:
042: import sun.security.util.*;
043: import sun.security.x509.AlgorithmId;
044: import sun.security.x509.X509Key;
045: import sun.security.x509.X500Name;
046: import sun.security.x509.X500Signer;
047:
048: /**
049: * A PKCS #10 certificate request is created and sent to a Certificate
050: * Authority, which then creates an X.509 certificate and returns it to
051: * the entity that requested it. A certificate request basically consists
052: * of the subject's X.500 name, public key, and optionally some attributes,
053: * signed using the corresponding private key.
054: *
055: * The ASN.1 syntax for a Certification Request is:
056: * <pre>
057: * CertificationRequest ::= SEQUENCE {
058: * certificationRequestInfo CertificationRequestInfo,
059: * signatureAlgorithm SignatureAlgorithmIdentifier,
060: * signature Signature
061: * }
062: *
063: * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
064: * Signature ::= BIT STRING
065: *
066: * CertificationRequestInfo ::= SEQUENCE {
067: * version Version,
068: * subject Name,
069: * subjectPublicKeyInfo SubjectPublicKeyInfo,
070: * attributes [0] IMPLICIT Attributes
071: * }
072: * Attributes ::= SET OF Attribute
073: * </pre>
074: *
075: * @author David Brownell
076: * @author Amit Kapoor
077: * @author Hemma Prafullchandra
078: * @version 1.45
079: */
080: public class PKCS10 {
081: /**
082: * Constructs an unsigned PKCS #10 certificate request. Before this
083: * request may be used, it must be encoded and signed. Then it
084: * must be retrieved in some conventional format (e.g. string).
085: *
086: * @param publicKey the public key that should be placed
087: * into the certificate generated by the CA.
088: */
089: public PKCS10(PublicKey publicKey) {
090: subjectPublicKeyInfo = publicKey;
091: attributeSet = new PKCS10Attributes();
092: }
093:
094: /**
095: * Constructs an unsigned PKCS #10 certificate request. Before this
096: * request may be used, it must be encoded and signed. Then it
097: * must be retrieved in some conventional format (e.g. string).
098: *
099: * @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) throws IOException, SignatureException,
122: 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(
177: "Invalid PKCS #10 signature");
178: } catch (InvalidKeyException e) {
179: throw new SignatureException("invalid key");
180: }
181: }
182:
183: /**
184: * Create the signed certificate request. This will later be
185: * retrieved in either string or binary format.
186: *
187: * @param requester identifies the signer (by X.500 name)
188: * and provides the private key used to sign.
189: * @exception IOException on errors.
190: * @exception CertificateException on certificate handling errors.
191: * @exception SignatureException on signature handling errors.
192: */
193: public void encodeAndSign(X500Signer requester)
194: throws CertificateException, IOException,
195: SignatureException {
196: DerOutputStream out, scratch;
197: byte[] certificateRequestInfo;
198: byte[] sig;
199:
200: if (encoded != null)
201: throw new SignatureException("request is already signed");
202:
203: subject = requester.getSigner();
204:
205: /*
206: * Encode cert request info, wrap in a sequence for signing
207: */
208: scratch = new DerOutputStream();
209: scratch.putInteger(BigInteger.ZERO); // PKCS #10 v1.0
210: subject.encode(scratch); // X.500 name
211: scratch.write(subjectPublicKeyInfo.getEncoded()); // public key
212: attributeSet.encode(scratch);
213:
214: out = new DerOutputStream();
215: out.write(DerValue.tag_Sequence, scratch); // wrap it!
216: certificateRequestInfo = out.toByteArray();
217: scratch = out;
218:
219: /*
220: * Sign it ...
221: */
222: requester.update(certificateRequestInfo, 0,
223: certificateRequestInfo.length);
224: sig = requester.sign();
225:
226: /*
227: * Build guts of SIGNED macro
228: */
229: requester.getAlgorithmId().encode(scratch); // sig algorithm
230: scratch.putBitString(sig); // sig
231:
232: /*
233: * Wrap those guts in a sequence
234: */
235: out = new DerOutputStream();
236: out.write(DerValue.tag_Sequence, scratch);
237: encoded = out.toByteArray();
238: }
239:
240: /**
241: * Returns the subject's name.
242: */
243: public X500Name getSubjectName() {
244: return subject;
245: }
246:
247: /**
248: * Returns the subject's public key.
249: */
250: public PublicKey getSubjectPublicKeyInfo() {
251: return subjectPublicKeyInfo;
252: }
253:
254: /**
255: * Returns the additional attributes requested.
256: */
257: public PKCS10Attributes getAttributes() {
258: return attributeSet;
259: }
260:
261: /**
262: * Returns the encoded and signed certificate request as a
263: * DER-encoded byte array.
264: *
265: * @return the certificate request, or null if encodeAndSign()
266: * has not yet been called.
267: */
268: public byte[] getEncoded() {
269: if (encoded != null)
270: return (byte[]) encoded.clone();
271: else
272: return null;
273: }
274:
275: /**
276: * Prints an E-Mailable version of the certificate request on the print
277: * stream passed. The format is a common base64 encoded one, supported
278: * by most Certificate Authorities because Netscape web servers have
279: * used this for some time. Some certificate authorities expect some
280: * more information, in particular contact information for the web
281: * server administrator.
282: *
283: * @param out the print stream where the certificate request
284: * will be printed.
285: * @exception IOException when an output operation failed
286: * @exception SignatureException when the certificate request was
287: * not yet signed.
288: */
289: public void print(PrintStream out) throws IOException,
290: SignatureException {
291: if (encoded == null)
292: throw new SignatureException("Cert request was not signed");
293:
294: BASE64Encoder encoder = new BASE64Encoder();
295:
296: out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");
297: encoder.encodeBuffer(encoded, out);
298: out.println("-----END NEW CERTIFICATE REQUEST-----");
299: }
300:
301: /**
302: * Provides a short description of this request.
303: */
304: public String toString() {
305: return "[PKCS #10 certificate request:\n"
306: + subjectPublicKeyInfo.toString() + " subject: <"
307: + subject + ">" + "\n" + " attributes: "
308: + attributeSet.toString() + "\n]";
309: }
310:
311: /**
312: * Compares this object for equality with the specified
313: * object. If the <code>other</code> object is an
314: * <code>instanceof</code> <code>PKCS10</code>, then
315: * its encoded form is retrieved and compared with the
316: * encoded form of this certificate request.
317: *
318: * @param other the object to test for equality with this object.
319: * @return true iff the encoded forms of the two certificate
320: * requests match, false otherwise.
321: */
322: public boolean equals(Object other) {
323: if (this == other)
324: return true;
325: if (!(other instanceof PKCS10))
326: return false;
327: if (encoded == null) // not signed yet
328: return false;
329: byte[] otherEncoded = ((PKCS10) other).getEncoded();
330: if (otherEncoded == null)
331: return false;
332:
333: return java.util.Arrays.equals(encoded, otherEncoded);
334: }
335:
336: /**
337: * Returns a hashcode value for this certificate request from its
338: * encoded form.
339: *
340: * @return the hashcode value.
341: */
342: public int hashCode() {
343: int retval = 0;
344: if (encoded != null)
345: for (int i = 1; i < encoded.length; i++)
346: retval += encoded[i] * i;
347: return (retval);
348: }
349:
350: private X500Name subject;
351: private PublicKey subjectPublicKeyInfo;
352: private PKCS10Attributes attributeSet;
353: private byte[] encoded; // signed
354: }
|