354 lines · 12.1 KB · Java
RawDownload
/* * Copyright (c) 1996, 2002, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */  package sun.security.pkcs; import java.io.ByteArrayOutputStream;import java.io.PrintStream;import java.io.IOException;import java.math.BigInteger; import java.security.cert.CertificateException;import java.security.NoSuchAlgorithmException;import java.security.InvalidKeyException;import java.security.Signature;import java.security.SignatureException;import java.security.PublicKey; import sun.misc.BASE64Encoder; import sun.security.util.*;import sun.security.x509.AlgorithmId;import sun.security.x509.X509Key;import sun.security.x509.X500Name; /** * A PKCS #10 certificate request is created and sent to a Certificate * Authority, which then creates an X.509 certificate and returns it to * the entity that requested it. A certificate request basically consists * of the subject's X.500 name, public key, and optionally some attributes, * signed using the corresponding private key. * * The ASN.1 syntax for a Certification Request is: * <pre> * CertificationRequest ::= SEQUENCE { *    certificationRequestInfo CertificationRequestInfo, *    signatureAlgorithm       SignatureAlgorithmIdentifier, *    signature                Signature *  } * * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier * Signature ::= BIT STRING * * CertificationRequestInfo ::= SEQUENCE { *    version                 Version, *    subject                 Name, *    subjectPublicKeyInfo    SubjectPublicKeyInfo, *    attributes [0] IMPLICIT Attributes * } * Attributes ::= SET OF Attribute * </pre> * * @author David Brownell * @author Amit Kapoor * @author Hemma Prafullchandra */public class PKCS10 {    /**     * Constructs an unsigned PKCS #10 certificate request.  Before this     * request may be used, it must be encoded and signed.  Then it     * must be retrieved in some conventional format (e.g. string).     *     * @param publicKey the public key that should be placed     *          into the certificate generated by the CA.     */    public PKCS10(PublicKey publicKey) {        subjectPublicKeyInfo = publicKey;        attributeSet = new PKCS10Attributes();    }     /**     * Constructs an unsigned PKCS #10 certificate request.  Before this     * request may be used, it must be encoded and signed.  Then it     * must be retrieved in some conventional format (e.g. string).     *     * @param publicKey the public key that should be placed     *          into the certificate generated by the CA.     * @param attributes additonal set of PKCS10 attributes requested     *          for in the certificate.     */    public PKCS10(PublicKey publicKey, PKCS10Attributes attributes) {        subjectPublicKeyInfo = publicKey;        attributeSet = attributes;    }     /**     * Parses an encoded, signed PKCS #10 certificate request, verifying     * the request's signature as it does so.  This constructor would     * typically be used by a Certificate Authority, from which a new     * certificate would then be constructed.     *     * @param data the DER-encoded PKCS #10 request.     * @exception IOException for low level errors reading the data     * @exception SignatureException when the signature is invalid     * @exception NoSuchAlgorithmException when the signature     *  algorithm is not supported in this environment     */    public PKCS10(byte[] data)    throws IOException, SignatureException, NoSuchAlgorithmException {        DerInputStream  in;        DerValue[]      seq;        AlgorithmId     id;        byte[]          sigData;        Signature       sig;         encoded = data;         //        // Outer sequence:  request, signature algorithm, signature.        // Parse, and prepare to verify later.        //        in = new DerInputStream(data);        seq = in.getSequence(3);         if (seq.length != 3)            throw new IllegalArgumentException("not a PKCS #10 request");         data = seq[0].toByteArray();            // reusing this variable        id = AlgorithmId.parse(seq[1]);        sigData = seq[2].getBitString();         //        // Inner sequence:  version, name, key, attributes        //        BigInteger      serial;        DerValue        val;         serial = seq[0].data.getBigInteger();        if (!serial.equals(BigInteger.ZERO))            throw new IllegalArgumentException("not PKCS #10 v1");         subject = new X500Name(seq[0].data);        subjectPublicKeyInfo = X509Key.parse(seq[0].data.getDerValue());         // Cope with a somewhat common illegal PKCS #10 format        if (seq[0].data.available() != 0)            attributeSet = new PKCS10Attributes(seq[0].data);        else            attributeSet = new PKCS10Attributes();         if (seq[0].data.available() != 0)            throw new IllegalArgumentException("illegal PKCS #10 data");         //        // OK, we parsed it all ... validate the signature using the        // key and signature algorithm we found.        //        try {            sig = Signature.getInstance(id.getName());            sig.initVerify(subjectPublicKeyInfo);            sig.update(data);            if (!sig.verify(sigData))                throw new SignatureException("Invalid PKCS #10 signature");        } catch (InvalidKeyException e) {            throw new SignatureException("invalid key");        }    }     /**     * Create the signed certificate request.  This will later be     * retrieved in either string or binary format.     *     * @param subject identifies the signer (by X.500 name).     * @param signature private key and signing algorithm to use.     * @exception IOException on errors.     * @exception CertificateException on certificate handling errors.     * @exception SignatureException on signature handling errors.     */    public void encodeAndSign(X500Name subject, Signature signature)    throws CertificateException, IOException, SignatureException {        DerOutputStream out, scratch;        byte[]          certificateRequestInfo;        byte[]          sig;         if (encoded != null)            throw new SignatureException("request is already signed");         this.subject = subject;         /*         * Encode cert request info, wrap in a sequence for signing         */        scratch = new DerOutputStream();        scratch.putInteger(BigInteger.ZERO);            // PKCS #10 v1.0        subject.encode(scratch);                        // X.500 name        scratch.write(subjectPublicKeyInfo.getEncoded()); // public key        attributeSet.encode(scratch);         out = new DerOutputStream();        out.write(DerValue.tag_Sequence, scratch);      // wrap it!        certificateRequestInfo = out.toByteArray();        scratch = out;         /*         * Sign it ...         */        signature.update(certificateRequestInfo, 0,                certificateRequestInfo.length);        sig = signature.sign();         /*         * Build guts of SIGNED macro         */        AlgorithmId algId = null;        try {            algId = AlgorithmId.getAlgorithmId(signature.getAlgorithm());        } catch (NoSuchAlgorithmException nsae) {            throw new SignatureException(nsae);        }        algId.encode(scratch);     // sig algorithm        scratch.putBitString(sig);                      // sig         /*         * Wrap those guts in a sequence         */        out = new DerOutputStream();        out.write(DerValue.tag_Sequence, scratch);        encoded = out.toByteArray();    }     /**     * Returns the subject's name.     */    public X500Name getSubjectName() { return subject; }     /**     * Returns the subject's public key.     */    public PublicKey getSubjectPublicKeyInfo()        { return subjectPublicKeyInfo; }     /**     * Returns the additional attributes requested.     */    public PKCS10Attributes getAttributes()        { return attributeSet; }     /**     * Returns the encoded and signed certificate request as a     * DER-encoded byte array.     *     * @return the certificate request, or null if encodeAndSign()     *          has not yet been called.     */    public byte[] getEncoded() {        if (encoded != null)            return encoded.clone();        else            return null;    }     /**     * Prints an E-Mailable version of the certificate request on the print     * stream passed.  The format is a common base64 encoded one, supported     * by most Certificate Authorities because Netscape web servers have     * used this for some time.  Some certificate authorities expect some     * more information, in particular contact information for the web     * server administrator.     *     * @param out the print stream where the certificate request     *  will be printed.     * @exception IOException when an output operation failed     * @exception SignatureException when the certificate request was     *  not yet signed.     */    public void print(PrintStream out)    throws IOException, SignatureException {        if (encoded == null)            throw new SignatureException("Cert request was not signed");         BASE64Encoder   encoder = new BASE64Encoder();         out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");        encoder.encodeBuffer(encoded, out);        out.println("-----END NEW CERTIFICATE REQUEST-----");    }     /**     * Provides a short description of this request.     */    public String toString() {        return "[PKCS #10 certificate request:\n"            + subjectPublicKeyInfo.toString()            + " subject: <" + subject + ">" + "\n"            + " attributes: " + attributeSet.toString()            + "\n]";    }     /**     * Compares this object for equality with the specified     * object. If the <code>other</code> object is an     * <code>instanceof</code> <code>PKCS10</code>, then     * its encoded form is retrieved and compared with the     * encoded form of this certificate request.     *     * @param other the object to test for equality with this object.     * @return true iff the encoded forms of the two certificate     * requests match, false otherwise.     */    public boolean equals(Object other) {        if (this == other)            return true;        if (!(other instanceof PKCS10))            return false;        if (encoded == null) // not signed yet            return false;        byte[] otherEncoded = ((PKCS10)other).getEncoded();        if (otherEncoded == null)            return false;         return java.util.Arrays.equals(encoded, otherEncoded);    }     /**     * Returns a hashcode value for this certificate request from its     * encoded form.     *     * @return the hashcode value.     */    public int hashCode() {        int     retval = 0;        if (encoded != null)            for (int i = 1; i < encoded.length; i++)             retval += encoded[i] * i;        return(retval);    }     private X500Name            subject;    private PublicKey           subjectPublicKeyInfo;    private PKCS10Attributes    attributeSet;    private byte[]              encoded;        // signed}