1 /* 2 * Copyright (c) 2000, 2012, 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 package sun.security.provider.certpath; 26 27 import java.io.IOException; 28 import java.security.GeneralSecurityException; 29 import java.security.PublicKey; 30 import java.security.cert.CertificateEncodingException; 31 import java.security.cert.CertificateException; 32 import java.security.cert.X509Certificate; 33 import java.security.interfaces.DSAPublicKey; 34 35 import javax.security.auth.x500.X500Principal; 36 37 import sun.security.util.DerOutputStream; 38 import sun.security.util.DerValue; 39 import sun.security.util.Cache; 40 import sun.security.x509.X509CertImpl; 41 import sun.security.provider.X509Factory; 42 43 /** 44 * This class represents an X.509 Certificate Pair object, which is primarily 45 * used to hold a pair of cross certificates issued between Certification 46 * Authorities. The ASN.1 structure is listed below. The forward certificate 47 * of the CertificatePair contains a certificate issued to this CA by another 48 * CA. The reverse certificate of the CertificatePair contains a certificate 49 * issued by this CA to another CA. When both the forward and the reverse 50 * certificates are present in the CertificatePair, the issuer name in one 51 * certificate shall match the subject name in the other and vice versa, and 52 * the subject public key in one certificate shall be capable of verifying the 53 * digital signature on the other certificate and vice versa. If a subject 54 * public key in one certificate does not contain required key algorithm 55 * parameters, then the signature check involving that key is not done.<p> 56 * 57 * The ASN.1 syntax for this object is: 58 * <pre> 59 * CertificatePair ::= SEQUENCE { 60 * forward [0] Certificate OPTIONAL, 61 * reverse [1] Certificate OPTIONAL 62 * -- at least one of the pair shall be present -- } 63 * </pre><p> 64 * 65 * This structure uses EXPLICIT tagging. References: Annex A of 66 * X.509(2000), X.509(1997). 67 * 68 * @author Sean Mullan 69 * @since 1.4 70 */ 71 72 public class X509CertificatePair { 73 74 /* ASN.1 explicit tags */ 75 private static final byte TAG_FORWARD = 0; 76 private static final byte TAG_REVERSE = 1; 77 78 private X509Certificate forward; 79 private X509Certificate reverse; 80 private byte[] encoded; 81 82 private static final Cache<Object, X509CertificatePair> cache 83 = Cache.newSoftMemoryCache(750); 84 85 /** 86 * Creates an empty instance of X509CertificatePair. 87 */ X509CertificatePair()88 public X509CertificatePair() {} 89 90 /** 91 * Creates an instance of X509CertificatePair. At least one of 92 * the pair must be non-null. 93 * 94 * @param forward The forward component of the certificate pair 95 * which represents a certificate issued to this CA by other CAs. 96 * @param reverse The reverse component of the certificate pair 97 * which represents a certificate issued by this CA to other CAs. 98 * @throws CertificateException If an exception occurs. 99 */ X509CertificatePair(X509Certificate forward, X509Certificate reverse)100 public X509CertificatePair(X509Certificate forward, X509Certificate reverse) 101 throws CertificateException { 102 if (forward == null && reverse == null) { 103 throw new CertificateException("at least one of certificate pair " 104 + "must be non-null"); 105 } 106 107 this.forward = forward; 108 this.reverse = reverse; 109 110 checkPair(); 111 } 112 113 /** 114 * Create a new X509CertificatePair from its encoding. 115 * 116 * For internal use only, external code should use generateCertificatePair. 117 */ X509CertificatePair(byte[] encoded)118 private X509CertificatePair(byte[] encoded) throws CertificateException { 119 try { 120 parse(new DerValue(encoded)); 121 this.encoded = encoded; 122 } catch (IOException ex) { 123 throw new CertificateException(ex.toString()); 124 } 125 checkPair(); 126 } 127 128 /** 129 * Clear the cache for debugging. 130 */ clearCache()131 public static synchronized void clearCache() { 132 cache.clear(); 133 } 134 135 /** 136 * Create a X509CertificatePair from its encoding. Uses cache lookup 137 * if possible. 138 */ generateCertificatePair(byte[] encoded)139 public static synchronized X509CertificatePair generateCertificatePair 140 (byte[] encoded) throws CertificateException { 141 Object key = new Cache.EqualByteArray(encoded); 142 X509CertificatePair pair = cache.get(key); 143 if (pair != null) { 144 return pair; 145 } 146 pair = new X509CertificatePair(encoded); 147 key = new Cache.EqualByteArray(pair.encoded); 148 cache.put(key, pair); 149 return pair; 150 } 151 152 /** 153 * Sets the forward component of the certificate pair. 154 */ setForward(X509Certificate cert)155 public void setForward(X509Certificate cert) throws CertificateException { 156 checkPair(); 157 forward = cert; 158 } 159 160 /** 161 * Sets the reverse component of the certificate pair. 162 */ setReverse(X509Certificate cert)163 public void setReverse(X509Certificate cert) throws CertificateException { 164 checkPair(); 165 reverse = cert; 166 } 167 168 /** 169 * Returns the forward component of the certificate pair. 170 * 171 * @return The forward certificate, or null if not set. 172 */ getForward()173 public X509Certificate getForward() { 174 return forward; 175 } 176 177 /** 178 * Returns the reverse component of the certificate pair. 179 * 180 * @return The reverse certificate, or null if not set. 181 */ getReverse()182 public X509Certificate getReverse() { 183 return reverse; 184 } 185 186 /** 187 * Return the DER encoded form of the certificate pair. 188 * 189 * @return The encoded form of the certificate pair. 190 * @throws CerticateEncodingException If an encoding exception occurs. 191 */ getEncoded()192 public byte[] getEncoded() throws CertificateEncodingException { 193 try { 194 if (encoded == null) { 195 DerOutputStream tmp = new DerOutputStream(); 196 emit(tmp); 197 encoded = tmp.toByteArray(); 198 } 199 } catch (IOException ex) { 200 throw new CertificateEncodingException(ex.toString()); 201 } 202 return encoded; 203 } 204 205 /** 206 * Return a printable representation of the certificate pair. 207 * 208 * @return A String describing the contents of the pair. 209 */ 210 @Override toString()211 public String toString() { 212 StringBuilder sb = new StringBuilder(); 213 sb.append("X.509 Certificate Pair: [\n"); 214 if (forward != null) 215 sb.append(" Forward: ").append(forward).append("\n"); 216 if (reverse != null) 217 sb.append(" Reverse: ").append(reverse).append("\n"); 218 sb.append("]"); 219 return sb.toString(); 220 } 221 222 /* Parse the encoded bytes */ parse(DerValue val)223 private void parse(DerValue val) 224 throws IOException, CertificateException 225 { 226 if (val.tag != DerValue.tag_Sequence) { 227 throw new IOException 228 ("Sequence tag missing for X509CertificatePair"); 229 } 230 231 while (val.data != null && val.data.available() != 0) { 232 DerValue opt = val.data.getDerValue(); 233 short tag = (byte) (opt.tag & 0x01f); 234 switch (tag) { 235 case TAG_FORWARD: 236 if (opt.isContextSpecific() && opt.isConstructed()) { 237 if (forward != null) { 238 throw new IOException("Duplicate forward " 239 + "certificate in X509CertificatePair"); 240 } 241 opt = opt.data.getDerValue(); 242 forward = X509Factory.intern 243 (new X509CertImpl(opt.toByteArray())); 244 } 245 break; 246 case TAG_REVERSE: 247 if (opt.isContextSpecific() && opt.isConstructed()) { 248 if (reverse != null) { 249 throw new IOException("Duplicate reverse " 250 + "certificate in X509CertificatePair"); 251 } 252 opt = opt.data.getDerValue(); 253 reverse = X509Factory.intern 254 (new X509CertImpl(opt.toByteArray())); 255 } 256 break; 257 default: 258 throw new IOException("Invalid encoding of " 259 + "X509CertificatePair"); 260 } 261 } 262 if (forward == null && reverse == null) { 263 throw new CertificateException("at least one of certificate pair " 264 + "must be non-null"); 265 } 266 } 267 268 /* Translate to encoded bytes */ emit(DerOutputStream out)269 private void emit(DerOutputStream out) 270 throws IOException, CertificateEncodingException 271 { 272 DerOutputStream tagged = new DerOutputStream(); 273 274 if (forward != null) { 275 DerOutputStream tmp = new DerOutputStream(); 276 tmp.putDerValue(new DerValue(forward.getEncoded())); 277 tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, 278 true, TAG_FORWARD), tmp); 279 } 280 281 if (reverse != null) { 282 DerOutputStream tmp = new DerOutputStream(); 283 tmp.putDerValue(new DerValue(reverse.getEncoded())); 284 tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, 285 true, TAG_REVERSE), tmp); 286 } 287 288 out.write(DerValue.tag_Sequence, tagged); 289 } 290 291 /* 292 * Check for a valid certificate pair 293 */ checkPair()294 private void checkPair() throws CertificateException { 295 296 /* if either of pair is missing, return w/o error */ 297 if (forward == null || reverse == null) { 298 return; 299 } 300 /* 301 * If both elements of the pair are present, check that they 302 * are a valid pair. 303 */ 304 X500Principal fwSubject = forward.getSubjectX500Principal(); 305 X500Principal fwIssuer = forward.getIssuerX500Principal(); 306 X500Principal rvSubject = reverse.getSubjectX500Principal(); 307 X500Principal rvIssuer = reverse.getIssuerX500Principal(); 308 if (!fwIssuer.equals(rvSubject) || !rvIssuer.equals(fwSubject)) { 309 throw new CertificateException("subject and issuer names in " 310 + "forward and reverse certificates do not match"); 311 } 312 313 /* check signatures unless key parameters are missing */ 314 try { 315 PublicKey pk = reverse.getPublicKey(); 316 if (!(pk instanceof DSAPublicKey) || 317 ((DSAPublicKey)pk).getParams() != null) { 318 forward.verify(pk); 319 } 320 pk = forward.getPublicKey(); 321 if (!(pk instanceof DSAPublicKey) || 322 ((DSAPublicKey)pk).getParams() != null) { 323 reverse.verify(pk); 324 } 325 } catch (GeneralSecurityException e) { 326 throw new CertificateException("invalid signature: " 327 + e.getMessage()); 328 } 329 } 330 } 331