1 /*
2  * Copyright (c) 2003, 2020, 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.security.util.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      */
CertId(X509Certificate issuerCert, SerialNumber serialNumber)72     public CertId(X509Certificate issuerCert, SerialNumber serialNumber)
73         throws IOException {
74 
75         this(issuerCert.getSubjectX500Principal(),
76              issuerCert.getPublicKey(), serialNumber);
77     }
78 
CertId(X500Principal issuerName, PublicKey issuerKey, SerialNumber serialNumber)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      */
CertId(DerInputStream derIn)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      */
getHashAlgorithm()128     public AlgorithmId getHashAlgorithm() {
129         return hashAlgId;
130     }
131 
132     /**
133      * Return the hash value for the issuer name.
134      */
getIssuerNameHash()135     public byte[] getIssuerNameHash() {
136         return issuerNameHash;
137     }
138 
139     /**
140      * Return the hash value for the issuer key.
141      */
getIssuerKeyHash()142     public byte[] getIssuerKeyHash() {
143         return issuerKeyHash;
144     }
145 
146     /**
147      * Return the serial number.
148      */
getSerialNumber()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      */
encode(DerOutputStream out)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      */
hashCode()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      */
equals(Object other)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      */
toString()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 }
236