| /* | |
| * Copyright (c) 1996, 2023, 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.*; | |
| import java.util.Arrays; | |
| import java.util.Base64; | |
| import sun.security.util.*; | |
| import sun.security.x509.AlgorithmId; | |
| import sun.security.x509.X509Key; | |
| import sun.security.x509.X500Name; | |
| import sun.security.util.SignatureUtil; | |
| /** | |
| * 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 additional 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; | |
| 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); | |
| SignatureUtil.initVerifyWithParam(sig, subjectPublicKeyInfo, | |
| SignatureUtil.getParamSpec(sigAlg, id.getParameters())); | |
| sig.update(data); | |
| if (!sig.verify(sigData)) { | |
| throw new SignatureException("Invalid PKCS #10 signature"); | |
| } | |
| } catch (InvalidKeyException e) { | |
| throw new SignatureException("Invalid key", e); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| throw new SignatureException("Invalid signature parameters", e); | |
| } catch (ProviderException e) { | |
| throw new SignatureException("Error parsing signature parameters", | |
| e.getCause()); | |
| } | |
| } | |
| /** | |
| * 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 key private key to use. | |
| * @param algorithm signing algorithm to use. | |
| * @exception IOException on errors. | |
| * @exception SignatureException on signature handling errors. | |
| * @exception NoSuchAlgorithmException algorithm is not recognized | |
| * @exception InvalidKeyException key has a problem | |
| */ | |
| public void encodeAndSign(X500Name subject, PrivateKey key, String algorithm) | |
| throws SignatureException, | |
| NoSuchAlgorithmException, InvalidKeyException { | |
| DerOutputStream out, scratch; | |
| byte[] certificateRequestInfo; | |
| byte[] sig; | |
| if (encoded != null) { | |
| throw new SignatureException("request is already signed"); | |
| } | |
| Signature signature = SignatureUtil.fromKey( | |
| algorithm, key, (Provider)null); | |
| 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.writeBytes(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 = SignatureUtil.fromSignature(signature, key); | |
| 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.print("-----BEGIN NEW CERTIFICATE REQUEST-----"); | |
| out.print("\r\n"); | |
| out.print(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded)); | |
| out.print("\r\n"); | |
| out.print("-----END NEW CERTIFICATE REQUEST-----"); | |
| out.print("\r\n"); | |
| } | |
| /** | |
| * 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>obj</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 obj the object to test for equality with this object. | |
| * @return true iff the encoded forms of the two certificate | |
| * requests match, false otherwise. | |
| */ | |
| @Override | |
| public boolean equals(Object obj) { | |
| if (this == obj) | |
| return true; | |
| if (!(obj instanceof PKCS10 other)) | |
| return false; | |
| if (encoded == null) // not signed yet | |
| return false; | |
| byte[] otherEncoded = other.getEncoded(); | |
| if (otherEncoded == null) | |
| return false; | |
| return Arrays.equals(encoded, otherEncoded); | |
| } | |
| /** | |
| * {@return the hashcode value for this certificate request from its | |
| * encoded form} | |
| */ | |
| @Override | |
| public int hashCode() { | |
| return Arrays.hashCode(encoded); | |
| } | |
| private X500Name subject; | |
| private final PublicKey subjectPublicKeyInfo; | |
| private String sigAlg; | |
| private final PKCS10Attributes attributeSet; | |
| private byte[] encoded; // signed | |
| } |