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