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