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
26package sun.security.provider.certpath;
27
28import java.io.IOException;
29import java.math.BigInteger;
30import java.security.MessageDigest;
31import java.security.NoSuchAlgorithmException;
32import java.security.PublicKey;
33import java.security.cert.X509Certificate;
34import java.util.Arrays;
35import javax.security.auth.x500.X500Principal;
36import sun.security.util.HexDumpEncoder;
37import sun.security.x509.*;
38import 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
58public 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}
236