diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/java.base/share/classes/sun/security/pkcs10/PKCS10.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,361 @@ +/* + * Copyright (c) 1996, 2013, 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.pkcs10; + +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 java.util.Base64; + +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: + *
+ * 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
+ * 
+ * + * @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 { + sigAlg = id.getName(); + sig = Signature.getInstance(sigAlg); + 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(); + sigAlg = signature.getAlgorithm(); + + /* + * Build guts of SIGNED macro + */ + AlgorithmId algId = null; + try { + algId = AlgorithmId.get(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 signature algorithm. + */ + public String getSigAlg() { return sigAlg; } + + /** + * 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"); + + + byte[] CRLF = new byte[] {'\r', '\n'}; + out.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); + out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded)); + 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 other object is an + * instanceof PKCS10, 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 String sigAlg; + private PKCS10Attributes attributeSet; + private byte[] encoded; // signed +}