0N/A/*
2362N/A * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/Apackage sun.security.provider.certpath;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.security.GeneralSecurityException;
0N/Aimport java.security.PublicKey;
0N/Aimport java.security.cert.CertificateEncodingException;
0N/Aimport java.security.cert.CertificateException;
0N/Aimport java.security.cert.X509Certificate;
0N/Aimport java.security.interfaces.DSAPublicKey;
0N/A
0N/Aimport javax.security.auth.x500.X500Principal;
0N/A
0N/Aimport sun.security.util.DerOutputStream;
0N/Aimport sun.security.util.DerValue;
0N/Aimport sun.security.util.Cache;
0N/Aimport sun.security.x509.X509CertImpl;
0N/Aimport sun.security.provider.X509Factory;
0N/A
0N/A/**
0N/A * This class represents an X.509 Certificate Pair object, which is primarily
0N/A * used to hold a pair of cross certificates issued between Certification
0N/A * Authorities. The ASN.1 structure is listed below. The forward certificate
0N/A * of the CertificatePair contains a certificate issued to this CA by another
0N/A * CA. The reverse certificate of the CertificatePair contains a certificate
0N/A * issued by this CA to another CA. When both the forward and the reverse
0N/A * certificates are present in the CertificatePair, the issuer name in one
0N/A * certificate shall match the subject name in the other and vice versa, and
0N/A * the subject public key in one certificate shall be capable of verifying the
0N/A * digital signature on the other certificate and vice versa. If a subject
0N/A * public key in one certificate does not contain required key algorithm
0N/A * parameters, then the signature check involving that key is not done.<p>
0N/A *
0N/A * The ASN.1 syntax for this object is:
0N/A * <pre>
0N/A * CertificatePair ::= SEQUENCE {
0N/A * forward [0] Certificate OPTIONAL,
0N/A * reverse [1] Certificate OPTIONAL
0N/A * -- at least one of the pair shall be present -- }
0N/A * </pre><p>
0N/A *
0N/A * This structure uses EXPLICIT tagging. References: Annex A of
0N/A * X.509(2000), X.509(1997).
0N/A *
0N/A * @author Sean Mullan
0N/A * @since 1.4
0N/A */
0N/A
0N/Apublic class X509CertificatePair {
0N/A
0N/A /* ASN.1 explicit tags */
0N/A private static final byte TAG_FORWARD = 0;
0N/A private static final byte TAG_REVERSE = 1;
0N/A
0N/A private X509Certificate forward;
0N/A private X509Certificate reverse;
0N/A private byte[] encoded;
0N/A
0N/A private static final Cache cache = Cache.newSoftMemoryCache(750);
0N/A
0N/A /**
0N/A * Creates an empty instance of X509CertificatePair.
0N/A */
0N/A public X509CertificatePair() {}
0N/A
0N/A /**
0N/A * Creates an instance of X509CertificatePair. At least one of
0N/A * the pair must be non-null.
0N/A *
0N/A * @param forward The forward component of the certificate pair
0N/A * which represents a certificate issued to this CA by other CAs.
0N/A * @param reverse The reverse component of the certificate pair
0N/A * which represents a certificate issued by this CA to other CAs.
0N/A * @throws CertificateException If an exception occurs.
0N/A */
0N/A public X509CertificatePair(X509Certificate forward, X509Certificate reverse)
0N/A throws CertificateException {
0N/A if (forward == null && reverse == null) {
0N/A throw new CertificateException("at least one of certificate pair "
0N/A + "must be non-null");
0N/A }
0N/A
0N/A this.forward = forward;
0N/A this.reverse = reverse;
0N/A
0N/A checkPair();
0N/A }
0N/A
0N/A /**
0N/A * Create a new X509CertificatePair from its encoding.
0N/A *
0N/A * For internal use only, external code should use generateCertificatePair.
0N/A */
0N/A private X509CertificatePair(byte[] encoded)throws CertificateException {
0N/A try {
0N/A parse(new DerValue(encoded));
0N/A this.encoded = encoded;
0N/A } catch (IOException ex) {
0N/A throw new CertificateException(ex.toString());
0N/A }
0N/A checkPair();
0N/A }
0N/A
0N/A /**
0N/A * Clear the cache for debugging.
0N/A */
0N/A public static synchronized void clearCache() {
0N/A cache.clear();
0N/A }
0N/A
0N/A /**
0N/A * Create a X509CertificatePair from its encoding. Uses cache lookup
0N/A * if possible.
0N/A */
0N/A public static synchronized X509CertificatePair generateCertificatePair
0N/A (byte[] encoded) throws CertificateException {
0N/A Object key = new Cache.EqualByteArray(encoded);
0N/A X509CertificatePair pair = (X509CertificatePair)cache.get(key);
0N/A if (pair != null) {
0N/A return pair;
0N/A }
0N/A pair = new X509CertificatePair(encoded);
0N/A key = new Cache.EqualByteArray(pair.encoded);
0N/A cache.put(key, pair);
0N/A return pair;
0N/A }
0N/A
0N/A /**
0N/A * Sets the forward component of the certificate pair.
0N/A */
0N/A public void setForward(X509Certificate cert) throws CertificateException {
0N/A checkPair();
0N/A forward = cert;
0N/A }
0N/A
0N/A /**
0N/A * Sets the reverse component of the certificate pair.
0N/A */
0N/A public void setReverse(X509Certificate cert) throws CertificateException {
0N/A checkPair();
0N/A reverse = cert;
0N/A }
0N/A
0N/A /**
0N/A * Returns the forward component of the certificate pair.
0N/A *
0N/A * @return The forward certificate, or null if not set.
0N/A */
0N/A public X509Certificate getForward() {
0N/A return forward;
0N/A }
0N/A
0N/A /**
0N/A * Returns the reverse component of the certificate pair.
0N/A *
0N/A * @return The reverse certificate, or null if not set.
0N/A */
0N/A public X509Certificate getReverse() {
0N/A return reverse;
0N/A }
0N/A
0N/A /**
0N/A * Return the DER encoded form of the certificate pair.
0N/A *
0N/A * @return The encoded form of the certificate pair.
0N/A * @throws CerticateEncodingException If an encoding exception occurs.
0N/A */
0N/A public byte[] getEncoded() throws CertificateEncodingException {
0N/A try {
0N/A if (encoded == null) {
0N/A DerOutputStream tmp = new DerOutputStream();
0N/A emit(tmp);
0N/A encoded = tmp.toByteArray();
0N/A }
0N/A } catch (IOException ex) {
0N/A throw new CertificateEncodingException(ex.toString());
0N/A }
0N/A return encoded;
0N/A }
0N/A
0N/A /**
0N/A * Return a printable representation of the certificate pair.
0N/A *
0N/A * @return A String describing the contents of the pair.
0N/A */
0N/A public String toString() {
0N/A StringBuffer sb = new StringBuffer();
0N/A sb.append("X.509 Certificate Pair: [\n");
0N/A if (forward != null)
0N/A sb.append(" Forward: " + forward + "\n");
0N/A if (reverse != null)
0N/A sb.append(" Reverse: " + reverse + "\n");
0N/A sb.append("]");
0N/A return sb.toString();
0N/A }
0N/A
0N/A /* Parse the encoded bytes */
0N/A private void parse(DerValue val)
0N/A throws IOException, CertificateException
0N/A {
0N/A if (val.tag != DerValue.tag_Sequence) {
0N/A throw new IOException
0N/A ("Sequence tag missing for X509CertificatePair");
0N/A }
0N/A
0N/A while (val.data != null && val.data.available() != 0) {
0N/A DerValue opt = val.data.getDerValue();
0N/A short tag = (byte) (opt.tag & 0x01f);
0N/A switch (tag) {
0N/A case TAG_FORWARD:
0N/A if (opt.isContextSpecific() && opt.isConstructed()) {
0N/A if (forward != null) {
0N/A throw new IOException("Duplicate forward "
0N/A + "certificate in X509CertificatePair");
0N/A }
0N/A opt = opt.data.getDerValue();
0N/A forward = X509Factory.intern
0N/A (new X509CertImpl(opt.toByteArray()));
0N/A }
0N/A break;
0N/A case TAG_REVERSE:
0N/A if (opt.isContextSpecific() && opt.isConstructed()) {
0N/A if (reverse != null) {
0N/A throw new IOException("Duplicate reverse "
0N/A + "certificate in X509CertificatePair");
0N/A }
0N/A opt = opt.data.getDerValue();
0N/A reverse = X509Factory.intern
0N/A (new X509CertImpl(opt.toByteArray()));
0N/A }
0N/A break;
0N/A default:
0N/A throw new IOException("Invalid encoding of "
0N/A + "X509CertificatePair");
0N/A }
0N/A }
0N/A if (forward == null && reverse == null) {
0N/A throw new CertificateException("at least one of certificate pair "
0N/A + "must be non-null");
0N/A }
0N/A }
0N/A
0N/A /* Translate to encoded bytes */
0N/A private void emit(DerOutputStream out)
0N/A throws IOException, CertificateEncodingException
0N/A {
0N/A DerOutputStream tagged = new DerOutputStream();
0N/A
0N/A if (forward != null) {
0N/A DerOutputStream tmp = new DerOutputStream();
0N/A tmp.putDerValue(new DerValue(forward.getEncoded()));
0N/A tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT,
0N/A true, TAG_FORWARD), tmp);
0N/A }
0N/A
0N/A if (reverse != null) {
0N/A DerOutputStream tmp = new DerOutputStream();
0N/A tmp.putDerValue(new DerValue(reverse.getEncoded()));
0N/A tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT,
0N/A true, TAG_REVERSE), tmp);
0N/A }
0N/A
0N/A out.write(DerValue.tag_Sequence, tagged);
0N/A }
0N/A
0N/A /*
0N/A * Check for a valid certificate pair
0N/A */
0N/A private void checkPair() throws CertificateException {
0N/A
0N/A /* if either of pair is missing, return w/o error */
0N/A if (forward == null || reverse == null) {
0N/A return;
0N/A }
0N/A /*
0N/A * If both elements of the pair are present, check that they
0N/A * are a valid pair.
0N/A */
0N/A X500Principal fwSubject = forward.getSubjectX500Principal();
0N/A X500Principal fwIssuer = forward.getIssuerX500Principal();
0N/A X500Principal rvSubject = reverse.getSubjectX500Principal();
0N/A X500Principal rvIssuer = reverse.getIssuerX500Principal();
0N/A if (!fwIssuer.equals(rvSubject) || !rvIssuer.equals(fwSubject)) {
0N/A throw new CertificateException("subject and issuer names in "
0N/A + "forward and reverse certificates do not match");
0N/A }
0N/A
0N/A /* check signatures unless key parameters are missing */
0N/A try {
0N/A PublicKey pk = reverse.getPublicKey();
0N/A if (!(pk instanceof DSAPublicKey) ||
0N/A ((DSAPublicKey)pk).getParams() != null) {
0N/A forward.verify(pk);
0N/A }
0N/A pk = forward.getPublicKey();
0N/A if (!(pk instanceof DSAPublicKey) ||
0N/A ((DSAPublicKey)pk).getParams() != null) {
0N/A reverse.verify(pk);
0N/A }
0N/A } catch (GeneralSecurityException e) {
0N/A throw new CertificateException("invalid signature: "
0N/A + e.getMessage());
0N/A }
0N/A }
0N/A}