1package org.bouncycastle.cert;
2
3import java.io.OutputStream;
4import java.math.BigInteger;
5import java.util.ArrayList;
6import java.util.List;
7
8import org.bouncycastle.asn1.ASN1Integer;
9import org.bouncycastle.asn1.ASN1ObjectIdentifier;
10import org.bouncycastle.asn1.ASN1Sequence;
11import org.bouncycastle.asn1.x500.X500Name;
12import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
13import org.bouncycastle.asn1.x509.GeneralName;
14import org.bouncycastle.asn1.x509.GeneralNames;
15import org.bouncycastle.asn1.x509.Holder;
16import org.bouncycastle.asn1.x509.IssuerSerial;
17import org.bouncycastle.asn1.x509.ObjectDigestInfo;
18import org.bouncycastle.operator.DigestCalculator;
19import org.bouncycastle.operator.DigestCalculatorProvider;
20import org.bouncycastle.util.Arrays;
21import org.bouncycastle.util.Selector;
22
23/**
24 * The Holder object.
25 *
26 * <pre>
27 *          Holder ::= SEQUENCE {
28 *                baseCertificateID   [0] IssuerSerial OPTIONAL,
29 *                         -- the issuer and serial number of
30 *                         -- the holder's Public Key Certificate
31 *                entityName          [1] GeneralNames OPTIONAL,
32 *                         -- the name of the claimant or role
33 *                objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
34 *                         -- used to directly authenticate the holder,
35 *                         -- for example, an executable
36 *          }
37 * </pre>
38 * <p>
39 * <b>Note:</b> If objectDigestInfo comparisons are to be carried out the static
40 * method setDigestCalculatorProvider <b>must</b> be called once to configure the class
41 * to do the necessary calculations.
42 * </p>
43 */
44public class AttributeCertificateHolder
45    implements Selector
46{
47    private static DigestCalculatorProvider digestCalculatorProvider;
48
49    final Holder holder;
50
51    AttributeCertificateHolder(ASN1Sequence seq)
52    {
53        holder = Holder.getInstance(seq);
54    }
55
56    public AttributeCertificateHolder(X500Name issuerName,
57        BigInteger serialNumber)
58    {
59        holder = new Holder(new IssuerSerial(
60            new GeneralNames(new GeneralName(issuerName)),
61            new ASN1Integer(serialNumber)));
62    }
63
64    public AttributeCertificateHolder(X509CertificateHolder cert)
65    {
66        holder = new Holder(new IssuerSerial(generateGeneralNames(cert.getIssuer()),
67            new ASN1Integer(cert.getSerialNumber())));
68    }
69
70    public AttributeCertificateHolder(X500Name principal)
71    {
72        holder = new Holder(generateGeneralNames(principal));
73    }
74
75    /**
76     * Constructs a holder for v2 attribute certificates with a hash value for
77     * some type of object.
78     * <p>
79     * <code>digestedObjectType</code> can be one of the following:
80     * <ul>
81     * <li>0 - publicKey - A hash of the public key of the holder must be
82     * passed.
83     * <li>1 - publicKeyCert - A hash of the public key certificate of the
84     * holder must be passed.
85     * <li>2 - otherObjectDigest - A hash of some other object type must be
86     * passed. <code>otherObjectTypeID</code> must not be empty.
87     * </ul>
88     * <p>
89     * This cannot be used if a v1 attribute certificate is used.
90     *
91     * @param digestedObjectType The digest object type.
92     * @param digestAlgorithm The algorithm identifier for the hash.
93     * @param otherObjectTypeID The object type ID if
94     *            <code>digestedObjectType</code> is
95     *            <code>otherObjectDigest</code>.
96     * @param objectDigest The hash value.
97     */
98    public AttributeCertificateHolder(int digestedObjectType,
99        ASN1ObjectIdentifier digestAlgorithm, ASN1ObjectIdentifier otherObjectTypeID, byte[] objectDigest)
100    {
101        holder = new Holder(new ObjectDigestInfo(digestedObjectType,
102            otherObjectTypeID, new AlgorithmIdentifier(digestAlgorithm), Arrays
103                .clone(objectDigest)));
104    }
105
106    /**
107     * Returns the digest object type if an object digest info is used.
108     * <p>
109     * <ul>
110     * <li>0 - publicKey - A hash of the public key of the holder must be
111     * passed.
112     * <li>1 - publicKeyCert - A hash of the public key certificate of the
113     * holder must be passed.
114     * <li>2 - otherObjectDigest - A hash of some other object type must be
115     * passed. <code>otherObjectTypeID</code> must not be empty.
116     * </ul>
117     *
118     * @return The digest object type or -1 if no object digest info is set.
119     */
120    public int getDigestedObjectType()
121    {
122        if (holder.getObjectDigestInfo() != null)
123        {
124            return holder.getObjectDigestInfo().getDigestedObjectType()
125                .getValue().intValue();
126        }
127        return -1;
128    }
129
130    /**
131     * Returns algorithm identifier for the digest used if ObjectDigestInfo is present.
132     *
133     * @return digest AlgorithmIdentifier or <code>null</code> if ObjectDigestInfo is absent.
134     */
135    public AlgorithmIdentifier getDigestAlgorithm()
136    {
137        if (holder.getObjectDigestInfo() != null)
138        {
139            return holder.getObjectDigestInfo().getDigestAlgorithm();
140        }
141        return null;
142    }
143
144    /**
145     * Returns the hash if an object digest info is used.
146     *
147     * @return The hash or <code>null</code> if ObjectDigestInfo is absent.
148     */
149    public byte[] getObjectDigest()
150    {
151        if (holder.getObjectDigestInfo() != null)
152        {
153            return holder.getObjectDigestInfo().getObjectDigest().getBytes();
154        }
155        return null;
156    }
157
158    /**
159     * Returns the digest algorithm ID if an object digest info is used.
160     *
161     * @return The digest algorithm ID or <code>null</code> if no object
162     *         digest info is set.
163     */
164    public ASN1ObjectIdentifier getOtherObjectTypeID()
165    {
166        if (holder.getObjectDigestInfo() != null)
167        {
168            new ASN1ObjectIdentifier(holder.getObjectDigestInfo().getOtherObjectTypeID().getId());
169        }
170        return null;
171    }
172
173    private GeneralNames generateGeneralNames(X500Name principal)
174    {
175        return new GeneralNames(new GeneralName(principal));
176    }
177
178    private boolean matchesDN(X500Name subject, GeneralNames targets)
179    {
180        GeneralName[] names = targets.getNames();
181
182        for (int i = 0; i != names.length; i++)
183        {
184            GeneralName gn = names[i];
185
186            if (gn.getTagNo() == GeneralName.directoryName)
187            {
188                if (X500Name.getInstance(gn.getName()).equals(subject))
189                {
190                    return true;
191                }
192            }
193        }
194
195        return false;
196    }
197
198    private X500Name[] getPrincipals(GeneralName[] names)
199    {
200        List l = new ArrayList(names.length);
201
202        for (int i = 0; i != names.length; i++)
203        {
204            if (names[i].getTagNo() == GeneralName.directoryName)
205            {
206                l.add(X500Name.getInstance(names[i].getName()));
207            }
208        }
209
210        return (X500Name[])l.toArray(new X500Name[l.size()]);
211    }
212
213    /**
214     * Return any principal objects inside the attribute certificate holder
215     * entity names field.
216     *
217     * @return an array of Principal objects (usually X500Principal), null if no
218     *         entity names field is set.
219     */
220    public X500Name[] getEntityNames()
221    {
222        if (holder.getEntityName() != null)
223        {
224            return getPrincipals(holder.getEntityName().getNames());
225        }
226
227        return null;
228    }
229
230    /**
231     * Return the principals associated with the issuer attached to this holder
232     *
233     * @return an array of principals, null if no BaseCertificateID is set.
234     */
235    public X500Name[] getIssuer()
236    {
237        if (holder.getBaseCertificateID() != null)
238        {
239            return getPrincipals(holder.getBaseCertificateID().getIssuer().getNames());
240        }
241
242        return null;
243    }
244
245    /**
246     * Return the serial number associated with the issuer attached to this
247     * holder.
248     *
249     * @return the certificate serial number, null if no BaseCertificateID is
250     *         set.
251     */
252    public BigInteger getSerialNumber()
253    {
254        if (holder.getBaseCertificateID() != null)
255        {
256            return holder.getBaseCertificateID().getSerial().getValue();
257        }
258
259        return null;
260    }
261
262    public Object clone()
263    {
264        return new AttributeCertificateHolder((ASN1Sequence)holder.toASN1Object());
265    }
266
267    public boolean match(Object obj)
268    {
269        if (!(obj instanceof X509CertificateHolder))
270        {
271            return false;
272        }
273
274        X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
275
276        if (holder.getBaseCertificateID() != null)
277        {
278            return holder.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
279                && matchesDN(x509Cert.getIssuer(), holder.getBaseCertificateID().getIssuer());
280        }
281
282        if (holder.getEntityName() != null)
283        {
284            if (matchesDN(x509Cert.getSubject(),
285                holder.getEntityName()))
286            {
287                return true;
288            }
289        }
290
291        if (holder.getObjectDigestInfo() != null)
292        {
293            try
294            {
295                DigestCalculator digCalc = digestCalculatorProvider.get(holder.getObjectDigestInfo().getDigestAlgorithm());
296                OutputStream     digOut = digCalc.getOutputStream();
297
298                switch (getDigestedObjectType())
299                {
300                case ObjectDigestInfo.publicKey:
301                    // TODO: DSA Dss-parms
302                    digOut.write(x509Cert.getSubjectPublicKeyInfo().getEncoded());
303                    break;
304                case ObjectDigestInfo.publicKeyCert:
305                    digOut.write(x509Cert.getEncoded());
306                    break;
307                }
308
309                digOut.close();
310
311                if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
312                {
313                    return false;
314                }
315            }
316            catch (Exception e)
317            {
318                return false;
319            }
320        }
321
322        return false;
323    }
324
325    public boolean equals(Object obj)
326    {
327        if (obj == this)
328        {
329            return true;
330        }
331
332        if (!(obj instanceof AttributeCertificateHolder))
333        {
334            return false;
335        }
336
337        AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
338
339        return this.holder.equals(other.holder);
340    }
341
342    public int hashCode()
343    {
344        return this.holder.hashCode();
345    }
346
347    /**
348     * Set a digest calculator provider to be used if matches are attempted using
349     * ObjectDigestInfo,
350     *
351     * @param digCalcProvider a provider of digest calculators.
352     */
353    public static void setDigestCalculatorProvider(DigestCalculatorProvider digCalcProvider)
354    {
355        digestCalculatorProvider = digCalcProvider;
356    }
357}
358