Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/X509CertificatePair.java
41161 views
/*1* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.security.provider.certpath;2526import java.io.IOException;27import java.security.GeneralSecurityException;28import java.security.PublicKey;29import java.security.cert.CertificateEncodingException;30import java.security.cert.CertificateException;31import java.security.cert.X509Certificate;32import java.security.interfaces.DSAPublicKey;3334import javax.security.auth.x500.X500Principal;3536import sun.security.util.DerOutputStream;37import sun.security.util.DerValue;38import sun.security.util.Cache;39import sun.security.x509.X509CertImpl;40import sun.security.provider.X509Factory;4142/**43* This class represents an X.509 Certificate Pair object, which is primarily44* used to hold a pair of cross certificates issued between Certification45* Authorities. The ASN.1 structure is listed below. The forward certificate46* of the CertificatePair contains a certificate issued to this CA by another47* CA. The reverse certificate of the CertificatePair contains a certificate48* issued by this CA to another CA. When both the forward and the reverse49* certificates are present in the CertificatePair, the issuer name in one50* certificate shall match the subject name in the other and vice versa, and51* the subject public key in one certificate shall be capable of verifying the52* digital signature on the other certificate and vice versa. If a subject53* public key in one certificate does not contain required key algorithm54* parameters, then the signature check involving that key is not done.<p>55*56* The ASN.1 syntax for this object is:57* <pre>58* CertificatePair ::= SEQUENCE {59* forward [0] Certificate OPTIONAL,60* reverse [1] Certificate OPTIONAL61* -- at least one of the pair shall be present -- }62* </pre><p>63*64* This structure uses EXPLICIT tagging. References: Annex A of65* X.509(2000), X.509(1997).66*67* @author Sean Mullan68* @since 1.469*/7071public class X509CertificatePair {7273/* ASN.1 explicit tags */74private static final byte TAG_FORWARD = 0;75private static final byte TAG_REVERSE = 1;7677private X509Certificate forward;78private X509Certificate reverse;79private byte[] encoded;8081private static final Cache<Object, X509CertificatePair> cache82= Cache.newSoftMemoryCache(750);8384/**85* Creates an empty instance of X509CertificatePair.86*/87public X509CertificatePair() {}8889/**90* Creates an instance of X509CertificatePair. At least one of91* the pair must be non-null.92*93* @param forward The forward component of the certificate pair94* which represents a certificate issued to this CA by other CAs.95* @param reverse The reverse component of the certificate pair96* which represents a certificate issued by this CA to other CAs.97* @throws CertificateException If an exception occurs.98*/99public X509CertificatePair(X509Certificate forward, X509Certificate reverse)100throws CertificateException {101if (forward == null && reverse == null) {102throw new CertificateException("at least one of certificate pair "103+ "must be non-null");104}105106this.forward = forward;107this.reverse = reverse;108109checkPair();110}111112/**113* Create a new X509CertificatePair from its encoding.114*115* For internal use only, external code should use generateCertificatePair.116*/117private X509CertificatePair(byte[] encoded) throws CertificateException {118try {119parse(new DerValue(encoded));120this.encoded = encoded;121} catch (IOException ex) {122throw new CertificateException(ex.toString());123}124checkPair();125}126127/**128* Clear the cache for debugging.129*/130public static synchronized void clearCache() {131cache.clear();132}133134/**135* Create a X509CertificatePair from its encoding. Uses cache lookup136* if possible.137*/138public static synchronized X509CertificatePair generateCertificatePair139(byte[] encoded) throws CertificateException {140Object key = new Cache.EqualByteArray(encoded);141X509CertificatePair pair = cache.get(key);142if (pair != null) {143return pair;144}145pair = new X509CertificatePair(encoded);146key = new Cache.EqualByteArray(pair.encoded);147cache.put(key, pair);148return pair;149}150151/**152* Sets the forward component of the certificate pair.153*/154public void setForward(X509Certificate cert) throws CertificateException {155checkPair();156forward = cert;157}158159/**160* Sets the reverse component of the certificate pair.161*/162public void setReverse(X509Certificate cert) throws CertificateException {163checkPair();164reverse = cert;165}166167/**168* Returns the forward component of the certificate pair.169*170* @return The forward certificate, or null if not set.171*/172public X509Certificate getForward() {173return forward;174}175176/**177* Returns the reverse component of the certificate pair.178*179* @return The reverse certificate, or null if not set.180*/181public X509Certificate getReverse() {182return reverse;183}184185/**186* Return the DER encoded form of the certificate pair.187*188* @return The encoded form of the certificate pair.189* @throws CerticateEncodingException If an encoding exception occurs.190*/191public byte[] getEncoded() throws CertificateEncodingException {192try {193if (encoded == null) {194DerOutputStream tmp = new DerOutputStream();195emit(tmp);196encoded = tmp.toByteArray();197}198} catch (IOException ex) {199throw new CertificateEncodingException(ex.toString());200}201return encoded;202}203204/**205* Return a printable representation of the certificate pair.206*207* @return A String describing the contents of the pair.208*/209@Override210public String toString() {211StringBuilder sb = new StringBuilder();212sb.append("X.509 Certificate Pair: [\n");213if (forward != null)214sb.append(" Forward: ").append(forward).append("\n");215if (reverse != null)216sb.append(" Reverse: ").append(reverse).append("\n");217sb.append("]");218return sb.toString();219}220221/* Parse the encoded bytes */222private void parse(DerValue val)223throws IOException, CertificateException224{225if (val.tag != DerValue.tag_Sequence) {226throw new IOException227("Sequence tag missing for X509CertificatePair");228}229230while (val.data != null && val.data.available() != 0) {231DerValue opt = val.data.getDerValue();232short tag = (byte) (opt.tag & 0x01f);233switch (tag) {234case TAG_FORWARD:235if (opt.isContextSpecific() && opt.isConstructed()) {236if (forward != null) {237throw new IOException("Duplicate forward "238+ "certificate in X509CertificatePair");239}240opt = opt.data.getDerValue();241forward = X509Factory.intern242(new X509CertImpl(opt.toByteArray()));243}244break;245case TAG_REVERSE:246if (opt.isContextSpecific() && opt.isConstructed()) {247if (reverse != null) {248throw new IOException("Duplicate reverse "249+ "certificate in X509CertificatePair");250}251opt = opt.data.getDerValue();252reverse = X509Factory.intern253(new X509CertImpl(opt.toByteArray()));254}255break;256default:257throw new IOException("Invalid encoding of "258+ "X509CertificatePair");259}260}261if (forward == null && reverse == null) {262throw new CertificateException("at least one of certificate pair "263+ "must be non-null");264}265}266267/* Translate to encoded bytes */268private void emit(DerOutputStream out)269throws IOException, CertificateEncodingException270{271DerOutputStream tagged = new DerOutputStream();272273if (forward != null) {274DerOutputStream tmp = new DerOutputStream();275tmp.putDerValue(new DerValue(forward.getEncoded()));276tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT,277true, TAG_FORWARD), tmp);278}279280if (reverse != null) {281DerOutputStream tmp = new DerOutputStream();282tmp.putDerValue(new DerValue(reverse.getEncoded()));283tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT,284true, TAG_REVERSE), tmp);285}286287out.write(DerValue.tag_Sequence, tagged);288}289290/*291* Check for a valid certificate pair292*/293private void checkPair() throws CertificateException {294295/* if either of pair is missing, return w/o error */296if (forward == null || reverse == null) {297return;298}299/*300* If both elements of the pair are present, check that they301* are a valid pair.302*/303X500Principal fwSubject = forward.getSubjectX500Principal();304X500Principal fwIssuer = forward.getIssuerX500Principal();305X500Principal rvSubject = reverse.getSubjectX500Principal();306X500Principal rvIssuer = reverse.getIssuerX500Principal();307if (!fwIssuer.equals(rvSubject) || !rvIssuer.equals(fwSubject)) {308throw new CertificateException("subject and issuer names in "309+ "forward and reverse certificates do not match");310}311312/* check signatures unless key parameters are missing */313try {314PublicKey pk = reverse.getPublicKey();315if (!(pk instanceof DSAPublicKey) ||316((DSAPublicKey)pk).getParams() != null) {317forward.verify(pk);318}319pk = forward.getPublicKey();320if (!(pk instanceof DSAPublicKey) ||321((DSAPublicKey)pk).getParams() != null) {322reverse.verify(pk);323}324} catch (GeneralSecurityException e) {325throw new CertificateException("invalid signature: "326+ e.getMessage());327}328}329}330331332