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 }