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 
  27 package sun.security.pkcs10;
  28 
  29 import java.io.PrintStream;
  30 import java.io.IOException;
  31 import java.math.BigInteger;
  32 
  33 import java.security.cert.CertificateException;
  34 import java.security.NoSuchAlgorithmException;
  35 import java.security.InvalidKeyException;
  36 import java.security.Signature;
  37 import java.security.SignatureException;
  38 import java.security.PublicKey;
  39 
  40 import java.util.Base64;
  41 
  42 import sun.security.util.*;
  43 import sun.security.x509.AlgorithmId;
  44 import sun.security.x509.X509Key;
  45 import 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  */
  78 public 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             sigAlg = id.getName();
 171             sig = Signature.getInstance(sigAlg);
 172             sig.initVerify(subjectPublicKeyInfo);
 173             sig.update(data);
 174             if (!sig.verify(sigData))
 175                 throw new SignatureException("Invalid PKCS #10 signature");
 176         } catch (InvalidKeyException e) {
 177             throw new SignatureException("invalid key");
 178         }
 179     }
 180 
 181     /**
 182      * Create the signed certificate request.  This will later be
 183      * retrieved in either string or binary format.
 184      *
 185      * @param subject identifies the signer (by X.500 name).
 186      * @param signature private key and signing algorithm to use.
 187      * @exception IOException on errors.
 188      * @exception CertificateException on certificate handling errors.
 189      * @exception SignatureException on signature handling errors.
 190      */
 191     public void encodeAndSign(X500Name subject, Signature signature)
 192     throws CertificateException, IOException, SignatureException {
 193         DerOutputStream out, scratch;
 194         byte[]          certificateRequestInfo;
 195         byte[]          sig;
 196 
 197         if (encoded != null)
 198             throw new SignatureException("request is already signed");
 199 
 200         this.subject = subject;
 201 
 202         /*
 203          * Encode cert request info, wrap in a sequence for signing
 204          */
 205         scratch = new DerOutputStream();
 206         scratch.putInteger(BigInteger.ZERO);            // PKCS #10 v1.0
 207         subject.encode(scratch);                        // X.500 name
 208         scratch.write(subjectPublicKeyInfo.getEncoded()); // public key
 209         attributeSet.encode(scratch);
 210 
 211         out = new DerOutputStream();
 212         out.write(DerValue.tag_Sequence, scratch);      // wrap it!
 213         certificateRequestInfo = out.toByteArray();
 214         scratch = out;
 215 
 216         /*
 217          * Sign it ...
 218          */
 219         signature.update(certificateRequestInfo, 0,
 220                 certificateRequestInfo.length);
 221         sig = signature.sign();
 222         sigAlg = signature.getAlgorithm();
 223 
 224         /*
 225          * Build guts of SIGNED macro
 226          */
 227         AlgorithmId algId = null;
 228         try {
 229             algId = AlgorithmId.get(signature.getAlgorithm());
 230         } catch (NoSuchAlgorithmException nsae) {
 231             throw new SignatureException(nsae);
 232         }
 233         algId.encode(scratch);     // sig algorithm
 234         scratch.putBitString(sig);                      // sig
 235 
 236         /*
 237          * Wrap those guts in a sequence
 238          */
 239         out = new DerOutputStream();
 240         out.write(DerValue.tag_Sequence, scratch);
 241         encoded = out.toByteArray();
 242     }
 243 
 244     /**
 245      * Returns the subject's name.
 246      */
 247     public X500Name getSubjectName() { return subject; }
 248 
 249     /**
 250      * Returns the subject's public key.
 251      */
 252     public PublicKey getSubjectPublicKeyInfo()
 253         { return subjectPublicKeyInfo; }
 254 
 255     /**
 256      * Returns the signature algorithm.
 257      */
 258     public String getSigAlg() { return sigAlg; }
 259 
 260     /**
 261      * Returns the additional attributes requested.
 262      */
 263     public PKCS10Attributes getAttributes()
 264         { return attributeSet; }
 265 
 266     /**
 267      * Returns the encoded and signed certificate request as a
 268      * DER-encoded byte array.
 269      *
 270      * @return the certificate request, or null if encodeAndSign()
 271      *          has not yet been called.
 272      */
 273     public byte[] getEncoded() {
 274         if (encoded != null)
 275             return encoded.clone();
 276         else
 277             return null;
 278     }
 279 
 280     /**
 281      * Prints an E-Mailable version of the certificate request on the print
 282      * stream passed.  The format is a common base64 encoded one, supported
 283      * by most Certificate Authorities because Netscape web servers have
 284      * used this for some time.  Some certificate authorities expect some
 285      * more information, in particular contact information for the web
 286      * server administrator.
 287      *
 288      * @param out the print stream where the certificate request
 289      *  will be printed.
 290      * @exception IOException when an output operation failed
 291      * @exception SignatureException when the certificate request was
 292      *  not yet signed.
 293      */
 294     public void print(PrintStream out)
 295     throws IOException, SignatureException {
 296         if (encoded == null)
 297             throw new SignatureException("Cert request was not signed");
 298 
 299 
 300         byte[] CRLF = new byte[] {'\r', '\n'};
 301         out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");
 302         out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded));
 303         out.println("-----END NEW CERTIFICATE REQUEST-----");
 304     }
 305 
 306     /**
 307      * Provides a short description of this request.
 308      */
 309     public String toString() {
 310         return "[PKCS #10 certificate request:\n"
 311             + subjectPublicKeyInfo.toString()
 312             + " subject: <" + subject + ">" + "\n"
 313             + " attributes: " + attributeSet.toString()
 314             + "\n]";
 315     }
 316 
 317     /**
 318      * Compares this object for equality with the specified
 319      * object. If the <code>other</code> object is an
 320      * <code>instanceof</code> <code>PKCS10</code>, then
 321      * its encoded form is retrieved and compared with the
 322      * encoded form of this certificate request.
 323      *
 324      * @param other the object to test for equality with this object.
 325      * @return true iff the encoded forms of the two certificate
 326      * requests match, false otherwise.
 327      */
 328     public boolean equals(Object other) {
 329         if (this == other)
 330             return true;
 331         if (!(other instanceof PKCS10))
 332             return false;
 333         if (encoded == null) // not signed yet
 334             return false;
 335         byte[] otherEncoded = ((PKCS10)other).getEncoded();
 336         if (otherEncoded == null)
 337             return false;
 338 
 339         return java.util.Arrays.equals(encoded, otherEncoded);
 340     }
 341 
 342     /**
 343      * Returns a hashcode value for this certificate request from its
 344      * encoded form.
 345      *
 346      * @return the hashcode value.
 347      */
 348     public int hashCode() {
 349         int     retval = 0;
 350         if (encoded != null)
 351             for (int i = 1; i < encoded.length; i++)
 352              retval += encoded[i] * i;
 353         return(retval);
 354     }
 355 
 356     private X500Name            subject;
 357     private PublicKey           subjectPublicKeyInfo;
 358     private String              sigAlg;
 359     private PKCS10Attributes    attributeSet;
 360     private byte[]              encoded;        // signed
 361 }