1 /*
   2  * Copyright (c) 2003, 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 package sun.security.provider.certpath;
  27 
  28 import java.io.IOException;
  29 import java.math.BigInteger;
  30 import java.security.MessageDigest;
  31 import java.security.NoSuchAlgorithmException;
  32 import java.security.PublicKey;
  33 import java.security.cert.X509Certificate;
  34 import java.util.Arrays;
  35 import javax.security.auth.x500.X500Principal;
  36 import sun.misc.HexDumpEncoder;
  37 import sun.security.x509.*;
  38 import sun.security.util.*;
  39 
  40 /**
  41  * This class corresponds to the CertId field in OCSP Request
  42  * and the OCSP Response. The ASN.1 definition for CertID is defined
  43  * in RFC 2560 as:
  44  * <pre>
  45  *
  46  * CertID          ::=     SEQUENCE {
  47  *      hashAlgorithm       AlgorithmIdentifier,
  48  *      issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
  49  *      issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
  50  *      serialNumber        CertificateSerialNumber
  51  *      }
  52  *
  53  * </pre>
  54  *
  55  * @author      Ram Marti
  56  */
  57 
  58 public class CertId {
  59 
  60     private static final boolean debug = false;
  61     private static final AlgorithmId SHA1_ALGID
  62         = new AlgorithmId(AlgorithmId.SHA_oid);
  63     private final AlgorithmId hashAlgId;
  64     private final byte[] issuerNameHash;
  65     private final byte[] issuerKeyHash;
  66     private final SerialNumber certSerialNumber;
  67     private int myhash = -1; // hashcode for this CertId
  68 
  69     /**
  70      * Creates a CertId. The hash algorithm used is SHA-1.
  71      */
  72     public CertId(X509Certificate issuerCert, SerialNumber serialNumber)
  73         throws IOException {
  74 
  75         this(issuerCert.getSubjectX500Principal(),
  76              issuerCert.getPublicKey(), serialNumber);
  77     }
  78 
  79     public CertId(X500Principal issuerName, PublicKey issuerKey,
  80                   SerialNumber serialNumber) throws IOException {
  81 
  82         // compute issuerNameHash
  83         MessageDigest md = null;
  84         try {
  85             md = MessageDigest.getInstance("SHA1");
  86         } catch (NoSuchAlgorithmException nsae) {
  87             throw new IOException("Unable to create CertId", nsae);
  88         }
  89         hashAlgId = SHA1_ALGID;
  90         md.update(issuerName.getEncoded());
  91         issuerNameHash = md.digest();
  92 
  93         // compute issuerKeyHash (remove the tag and length)
  94         byte[] pubKey = issuerKey.getEncoded();
  95         DerValue val = new DerValue(pubKey);
  96         DerValue[] seq = new DerValue[2];
  97         seq[0] = val.data.getDerValue(); // AlgorithmID
  98         seq[1] = val.data.getDerValue(); // Key
  99         byte[] keyBytes = seq[1].getBitString();
 100         md.update(keyBytes);
 101         issuerKeyHash = md.digest();
 102         certSerialNumber = serialNumber;
 103 
 104         if (debug) {
 105             HexDumpEncoder encoder = new HexDumpEncoder();
 106             System.out.println("Issuer Name is " + issuerName);
 107             System.out.println("issuerNameHash is " +
 108                 encoder.encodeBuffer(issuerNameHash));
 109             System.out.println("issuerKeyHash is " +
 110                 encoder.encodeBuffer(issuerKeyHash));
 111             System.out.println("SerialNumber is " + serialNumber.getNumber());
 112         }
 113     }
 114 
 115     /**
 116      * Creates a CertId from its ASN.1 DER encoding.
 117      */
 118     public CertId(DerInputStream derIn) throws IOException {
 119         hashAlgId = AlgorithmId.parse(derIn.getDerValue());
 120         issuerNameHash = derIn.getOctetString();
 121         issuerKeyHash = derIn.getOctetString();
 122         certSerialNumber = new SerialNumber(derIn);
 123     }
 124 
 125     /**
 126      * Return the hash algorithm identifier.
 127      */
 128     public AlgorithmId getHashAlgorithm() {
 129         return hashAlgId;
 130     }
 131 
 132     /**
 133      * Return the hash value for the issuer name.
 134      */
 135     public byte[] getIssuerNameHash() {
 136         return issuerNameHash;
 137     }
 138 
 139     /**
 140      * Return the hash value for the issuer key.
 141      */
 142     public byte[] getIssuerKeyHash() {
 143         return issuerKeyHash;
 144     }
 145 
 146     /**
 147      * Return the serial number.
 148      */
 149     public BigInteger getSerialNumber() {
 150         return certSerialNumber.getNumber();
 151     }
 152 
 153     /**
 154      * Encode the CertId using ASN.1 DER.
 155      * The hash algorithm used is SHA-1.
 156      */
 157     public void encode(DerOutputStream out) throws IOException {
 158 
 159         DerOutputStream tmp = new DerOutputStream();
 160         hashAlgId.encode(tmp);
 161         tmp.putOctetString(issuerNameHash);
 162         tmp.putOctetString(issuerKeyHash);
 163         certSerialNumber.encode(tmp);
 164         out.write(DerValue.tag_Sequence, tmp);
 165 
 166         if (debug) {
 167             HexDumpEncoder encoder = new HexDumpEncoder();
 168             System.out.println("Encoded certId is " +
 169                 encoder.encode(out.toByteArray()));
 170         }
 171     }
 172 
 173    /**
 174      * Returns a hashcode value for this CertId.
 175      *
 176      * @return the hashcode value.
 177      */
 178     @Override public int hashCode() {
 179         if (myhash == -1) {
 180             myhash = hashAlgId.hashCode();
 181             for (int i = 0; i < issuerNameHash.length; i++) {
 182                 myhash += issuerNameHash[i] * i;
 183             }
 184             for (int i = 0; i < issuerKeyHash.length; i++) {
 185                 myhash += issuerKeyHash[i] * i;
 186             }
 187             myhash += certSerialNumber.getNumber().hashCode();
 188         }
 189         return myhash;
 190     }
 191 
 192     /**
 193      * Compares this CertId for equality with the specified
 194      * object. Two CertId objects are considered equal if their hash algorithms,
 195      * their issuer name and issuer key hash values and their serial numbers
 196      * are equal.
 197      *
 198      * @param other the object to test for equality with this object.
 199      * @return true if the objects are considered equal, false otherwise.
 200      */
 201     @Override public boolean equals(Object other) {
 202         if (this == other) {
 203             return true;
 204         }
 205         if (other == null || (!(other instanceof CertId))) {
 206             return false;
 207         }
 208 
 209         CertId that = (CertId) other;
 210         if (hashAlgId.equals(that.getHashAlgorithm()) &&
 211             Arrays.equals(issuerNameHash, that.getIssuerNameHash()) &&
 212             Arrays.equals(issuerKeyHash, that.getIssuerKeyHash()) &&
 213             certSerialNumber.getNumber().equals(that.getSerialNumber())) {
 214             return true;
 215         } else {
 216             return false;
 217         }
 218     }
 219 
 220     /**
 221      * Create a string representation of the CertId.
 222      */
 223     @Override public String toString() {
 224         StringBuilder sb = new StringBuilder();
 225         sb.append("CertId \n");
 226         sb.append("Algorithm: " + hashAlgId.toString() +"\n");
 227         sb.append("issuerNameHash \n");
 228         HexDumpEncoder encoder = new HexDumpEncoder();
 229         sb.append(encoder.encode(issuerNameHash));
 230         sb.append("\nissuerKeyHash: \n");
 231         sb.append(encoder.encode(issuerKeyHash));
 232         sb.append("\n" +  certSerialNumber.toString());
 233         return sb.toString();
 234     }
 235 }