1 /*
   2  * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
   3  * 
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 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 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 package sun.security.ssl;
  26 
  27 import java.io.IOException;
  28 import java.security.cert.CertificateEncodingException;
  29 import java.security.cert.X509Certificate;
  30 import java.util.Arrays;
  31 import java.util.Objects;
  32 
  33 import javax.net.ssl.CertificateAuthority;
  34 
  35 import sun.security.util.DerOutputStream;
  36 import sun.security.util.DerValue;
  37 
  38 /**
  39  * Implementation for {@link CertificateAuthority} interface.
  40  * <p>
  41  * A {@code CertificateAuthorityImpl} instance can be constructed
  42  * by either:
  43  * <ul>
  44  * <li>providing a raw byte buffer to be parsed, or</li>
  45  * <li>providing a X.509 certificate.</li>
  46  * </ul>
  47  * <p>
  48  * When constructing an {@code CertificateAuthority} instance from
  49  * a raw byte buffer, the certificate authority distinguished name
  50  * is normalized to UTF-8 encoding for later comparisons.
  51  * <p>
  52  * {@code CertificateAuthorityImpl#implies(X509Certificate)} method
  53  * builds a new {@code CertificateAuthorityImpl} instance, based on the
  54  * X.509 certificate to be matched, and compares encoded byte arrays
  55  * to decide if the certificate was indicated.
  56  * <p>
  57  * See further information in
  58  * <a href="https://tools.ietf.org/html/draft-ietf-tls-tls13-20#section-4.2.4">TLS 1.3</a>.
  59  * 
  60  * @see CertificateAuthoritiesExtension
  61  * @see CertificateAuthority
  62  * 
  63  * @author Martin Balao (mbalao@redhat.com)
  64  */
  65 final class CertificateAuthorityImpl implements CertificateAuthority {
  66 
  67     private static final String exceptionMessage = "Certificate Authority internal exception.";
  68 
  69     private byte[] encodedData;
  70 
  71     CertificateAuthorityImpl(byte[] encodedData, int offset, int length) throws IOException {
  72 
  73         // Normalize the distinguished name in UTF-8 encoding
  74         // so we can later compare CertificateAuthority objects.
  75         final byte[] certificateDNDerEncoded = new byte[length];
  76         System.arraycopy(encodedData, offset, certificateDNDerEncoded, 0,
  77                 certificateDNDerEncoded.length);
  78         final DerValue certificateDNDerValue = new DerValue(certificateDNDerEncoded);
  79 
  80         byte[] certificateDNDerEncodedUTF8 = null;
  81         try (DerOutputStream out = new DerOutputStream()) {
  82             out.putUTF8String(certificateDNDerValue.getAsString());
  83             certificateDNDerEncodedUTF8 = out.toByteArray();
  84         }
  85 
  86         this.encodedData = new byte[2 + certificateDNDerEncodedUTF8.length];
  87         this.encodedData[0] = (byte) ((certificateDNDerEncodedUTF8.length & 0xFF00) >> 8);
  88         this.encodedData[1] = (byte) (certificateDNDerEncodedUTF8.length & 0xFF);
  89         System.arraycopy(certificateDNDerEncodedUTF8, 0, this.encodedData, 2,
  90                 certificateDNDerEncodedUTF8.length);
  91     }
  92 
  93     CertificateAuthorityImpl(X509Certificate trustedCertificate)
  94             throws CertificateEncodingException, Exception {
  95         try (DerOutputStream out = new DerOutputStream()) {
  96             out.putUTF8String(trustedCertificate.getSubjectDN().getName());
  97             final byte[] derEncodedName = out.toByteArray();
  98             if (derEncodedName.length <= 0 || derEncodedName.length > 0xFFFF) {
  99                 throw new IllegalArgumentException(
 100                         "Subject distinguished name on the trusted certificate has an invalid length.");
 101             }
 102             encodedData = new byte[2 + derEncodedName.length]; // 2 bytes for
 103                                                                     // data length
 104             encodedData[0] = (byte) ((derEncodedName.length & 0xFF00) >> 8);
 105             encodedData[1] = (byte) (derEncodedName.length & 0xFF);
 106             System.arraycopy(derEncodedName, 0, encodedData, 2, derEncodedName.length);
 107         } catch (IOException e) {
 108             throw new Exception(exceptionMessage, e);
 109         }
 110     }
 111 
 112     @Override
 113     public byte[] getEncoded() {
 114         return encodedData;
 115     }
 116 
 117     @Override
 118     public boolean implies(X509Certificate certificate)
 119             throws CertificateEncodingException, Exception {
 120         final CertificateAuthorityImpl certificateAuthority =
 121                 new CertificateAuthorityImpl(certificate);
 122         return certificateAuthority.equals(this);
 123     }
 124 
 125     @Override
 126     public boolean equals(Object o) {
 127         if (o.getClass().equals(this.getClass())) {
 128             return Arrays.equals(((CertificateAuthority) o).getEncoded(), this.getEncoded());
 129         }
 130         return false;
 131     }
 132 
 133     @Override
 134     public int hashCode() {
 135         return Objects.hashCode(encodedData);
 136     }
 137 
 138 }