1package org.bouncycastle.asn1.x509;
2
3import org.bouncycastle.asn1.ASN1Encodable;
4import org.bouncycastle.asn1.ASN1OctetString;
5import org.bouncycastle.asn1.ASN1TaggedObject;
6import org.bouncycastle.asn1.DERObject;
7import org.bouncycastle.asn1.DEROctetString;
8import org.bouncycastle.crypto.Digest;
9import org.bouncycastle.crypto.digests.SHA1Digest;
10
11/**
12 * The SubjectKeyIdentifier object.
13 * <pre>
14 * SubjectKeyIdentifier::= OCTET STRING
15 * </pre>
16 */
17public class SubjectKeyIdentifier
18    extends ASN1Encodable
19{
20    private byte[] keyidentifier;
21
22    public static SubjectKeyIdentifier getInstance(
23        ASN1TaggedObject obj,
24        boolean          explicit)
25    {
26        return getInstance(ASN1OctetString.getInstance(obj, explicit));
27    }
28
29    public static SubjectKeyIdentifier getInstance(
30        Object obj)
31    {
32        if (obj instanceof SubjectKeyIdentifier)
33        {
34            return (SubjectKeyIdentifier)obj;
35        }
36
37        if (obj instanceof SubjectPublicKeyInfo)
38        {
39            return new SubjectKeyIdentifier((SubjectPublicKeyInfo)obj);
40        }
41
42        if (obj instanceof ASN1OctetString)
43        {
44            return new SubjectKeyIdentifier((ASN1OctetString)obj);
45        }
46
47        if (obj instanceof X509Extension)
48        {
49            return getInstance(X509Extension.convertValueToObject((X509Extension)obj));
50        }
51
52        throw new IllegalArgumentException("Invalid SubjectKeyIdentifier: " + obj.getClass().getName());
53    }
54
55    public SubjectKeyIdentifier(
56        byte[] keyid)
57    {
58        this.keyidentifier=keyid;
59    }
60
61    public SubjectKeyIdentifier(
62        ASN1OctetString  keyid)
63    {
64        this.keyidentifier=keyid.getOctets();
65    }
66
67    /**
68     * Calculates the keyidentifier using a SHA1 hash over the BIT STRING
69     * from SubjectPublicKeyInfo as defined in RFC3280.
70     *
71     * @param spki the subject public key info.
72     */
73    public SubjectKeyIdentifier(
74        SubjectPublicKeyInfo    spki)
75    {
76        this.keyidentifier = getDigest(spki);
77    }
78
79    public byte[] getKeyIdentifier()
80    {
81        return keyidentifier;
82    }
83
84    public DERObject toASN1Object()
85    {
86        return new DEROctetString(keyidentifier);
87    }
88
89    /**
90     * Return a RFC 3280 type 1 key identifier. As in:
91     * <pre>
92     * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
93     * value of the BIT STRING subjectPublicKey (excluding the tag,
94     * length, and number of unused bits).
95     * </pre>
96     * @param keyInfo the key info object containing the subjectPublicKey field.
97     * @return the key identifier.
98     */
99    public static SubjectKeyIdentifier createSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo)
100    {
101        return new SubjectKeyIdentifier(keyInfo);
102    }
103
104    /**
105     * Return a RFC 3280 type 2 key identifier. As in:
106     * <pre>
107     * (2) The keyIdentifier is composed of a four bit type field with
108     * the value 0100 followed by the least significant 60 bits of the
109     * SHA-1 hash of the value of the BIT STRING subjectPublicKey.
110     * </pre>
111     * @param keyInfo the key info object containing the subjectPublicKey field.
112     * @return the key identifier.
113     */
114    public static SubjectKeyIdentifier createTruncatedSHA1KeyIdentifier(SubjectPublicKeyInfo keyInfo)
115    {
116        byte[] dig = getDigest(keyInfo);
117        byte[] id = new byte[8];
118
119        System.arraycopy(dig, dig.length - 8, id, 0, id.length);
120
121        id[0] &= 0x0f;
122        id[0] |= 0x40;
123
124        return new SubjectKeyIdentifier(id);
125    }
126
127    private static byte[] getDigest(SubjectPublicKeyInfo spki)
128    {
129        Digest digest = new SHA1Digest();
130        byte[]  resBuf = new byte[digest.getDigestSize()];
131
132        byte[] bytes = spki.getPublicKeyData().getBytes();
133        digest.update(bytes, 0, bytes.length);
134        digest.doFinal(resBuf, 0);
135        return resBuf;
136    }
137}
138