1package org.bouncycastle.operator.jcajce;
2
3import java.io.IOException;
4import java.io.OutputStream;
5import java.security.GeneralSecurityException;
6import java.security.Provider;
7import java.security.PublicKey;
8import java.security.Signature;
9import java.security.SignatureException;
10import java.security.cert.CertificateEncodingException;
11import java.security.cert.CertificateException;
12import java.security.cert.X509Certificate;
13
14import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
15import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
16import org.bouncycastle.cert.X509CertificateHolder;
17import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
18import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
19import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
20import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
21import org.bouncycastle.operator.ContentVerifier;
22import org.bouncycastle.operator.ContentVerifierProvider;
23import org.bouncycastle.operator.OperatorCreationException;
24import org.bouncycastle.operator.OperatorStreamException;
25import org.bouncycastle.operator.RawContentVerifier;
26import org.bouncycastle.operator.RuntimeOperatorException;
27
28public class JcaContentVerifierProviderBuilder
29{
30    private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
31
32    public JcaContentVerifierProviderBuilder()
33    {
34    }
35
36    public JcaContentVerifierProviderBuilder setProvider(Provider provider)
37    {
38        this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
39
40        return this;
41    }
42
43    public JcaContentVerifierProviderBuilder setProvider(String providerName)
44    {
45        this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
46
47        return this;
48    }
49
50    public ContentVerifierProvider build(X509CertificateHolder certHolder)
51        throws OperatorCreationException, CertificateException
52    {
53        return build(helper.convertCertificate(certHolder));
54    }
55
56    public ContentVerifierProvider build(final X509Certificate certificate)
57        throws OperatorCreationException
58    {
59        final X509CertificateHolder certHolder;
60
61        try
62        {
63            certHolder = new JcaX509CertificateHolder(certificate);
64        }
65        catch (CertificateEncodingException e)
66        {
67            throw new OperatorCreationException("cannot process certificate: " + e.getMessage(), e);
68        }
69
70        return new ContentVerifierProvider()
71        {
72            private SignatureOutputStream stream;
73
74            public boolean hasAssociatedCertificate()
75            {
76                return true;
77            }
78
79            public X509CertificateHolder getAssociatedCertificate()
80            {
81                return certHolder;
82            }
83
84            public ContentVerifier get(AlgorithmIdentifier algorithm)
85                throws OperatorCreationException
86            {
87                try
88                {
89                    Signature sig = helper.createSignature(algorithm);
90
91                    sig.initVerify(certificate.getPublicKey());
92
93                    stream = new SignatureOutputStream(sig);
94                }
95                catch (GeneralSecurityException e)
96                {
97                    throw new OperatorCreationException("exception on setup: " + e, e);
98                }
99
100                Signature rawSig = createRawSig(algorithm, certificate.getPublicKey());
101
102                if (rawSig != null)
103                {
104                    return new RawSigVerifier(algorithm, stream, rawSig);
105                }
106                else
107                {
108                    return new SigVerifier(algorithm, stream);
109                }
110            }
111        };
112    }
113
114    public ContentVerifierProvider build(final PublicKey publicKey)
115        throws OperatorCreationException
116    {
117        return new ContentVerifierProvider()
118        {
119            public boolean hasAssociatedCertificate()
120            {
121                return false;
122            }
123
124            public X509CertificateHolder getAssociatedCertificate()
125            {
126                return null;
127            }
128
129            public ContentVerifier get(AlgorithmIdentifier algorithm)
130                throws OperatorCreationException
131            {
132                SignatureOutputStream stream = createSignatureStream(algorithm, publicKey);
133
134                Signature rawSig = createRawSig(algorithm, publicKey);
135
136                if (rawSig != null)
137                {
138                    return new RawSigVerifier(algorithm, stream, rawSig);
139                }
140                else
141                {
142                    return new SigVerifier(algorithm, stream);
143                }
144            }
145        };
146    }
147
148    public ContentVerifierProvider build(SubjectPublicKeyInfo publicKey)
149        throws OperatorCreationException
150    {
151        return this.build(helper.convertPublicKey(publicKey));
152    }
153
154    private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey)
155        throws OperatorCreationException
156    {
157        try
158        {
159            Signature sig = helper.createSignature(algorithm);
160
161            sig.initVerify(publicKey);
162
163            return new SignatureOutputStream(sig);
164        }
165        catch (GeneralSecurityException e)
166        {
167            throw new OperatorCreationException("exception on setup: " + e, e);
168        }
169    }
170
171    private Signature createRawSig(AlgorithmIdentifier algorithm, PublicKey publicKey)
172    {
173        Signature rawSig;
174        try
175        {
176            rawSig = helper.createRawSignature(algorithm);
177
178            if (rawSig != null)
179            {
180                rawSig.initVerify(publicKey);
181            }
182        }
183        catch (Exception e)
184        {
185            rawSig = null;
186        }
187        return rawSig;
188    }
189
190    private class SigVerifier
191        implements ContentVerifier
192    {
193        private AlgorithmIdentifier algorithm;
194
195        protected SignatureOutputStream stream;
196
197        SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream)
198        {
199            this.algorithm = algorithm;
200            this.stream = stream;
201        }
202
203        public AlgorithmIdentifier getAlgorithmIdentifier()
204        {
205            return algorithm;
206        }
207
208        public OutputStream getOutputStream()
209        {
210            if (stream == null)
211            {
212                throw new IllegalStateException("verifier not initialised");
213            }
214
215            return stream;
216        }
217
218        public boolean verify(byte[] expected)
219        {
220            try
221            {
222                return stream.verify(expected);
223            }
224            catch (SignatureException e)
225            {
226                throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
227            }
228        }
229    }
230
231    private class RawSigVerifier
232        extends SigVerifier
233        implements RawContentVerifier
234    {
235        private Signature rawSignature;
236
237        RawSigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream, Signature rawSignature)
238        {
239            super(algorithm, stream);
240            this.rawSignature = rawSignature;
241        }
242
243        public boolean verify(byte[] expected)
244        {
245            try
246            {
247                return super.verify(expected);
248            }
249            finally
250            {
251                // we need to do this as in some PKCS11 implementations the session associated with the init of the
252                // raw signature will not be freed if verify is not called on it.
253                try
254                {
255                    rawSignature.verify(expected);
256                }
257                catch (Exception e)
258                {
259                    // ignore
260                }
261            }
262        }
263
264        public boolean verify(byte[] digest, byte[] expected)
265        {
266            try
267            {
268                rawSignature.update(digest);
269
270                return rawSignature.verify(expected);
271            }
272            catch (SignatureException e)
273            {
274                throw new RuntimeOperatorException("exception obtaining raw signature: " + e.getMessage(), e);
275            }
276            finally
277            {
278                // we need to do this as in some PKCS11 implementations the session associated with the init of the
279                // standard signature will not be freed if verify is not called on it.
280                try
281                {
282                    stream.verify(expected);
283                }
284                catch (Exception e)
285                {
286                    // ignore
287                }
288            }
289        }
290    }
291
292    private class SignatureOutputStream
293        extends OutputStream
294    {
295        private Signature sig;
296
297        SignatureOutputStream(Signature sig)
298        {
299            this.sig = sig;
300        }
301
302        public void write(byte[] bytes, int off, int len)
303            throws IOException
304        {
305            try
306            {
307                sig.update(bytes, off, len);
308            }
309            catch (SignatureException e)
310            {
311                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
312            }
313        }
314
315        public void write(byte[] bytes)
316            throws IOException
317        {
318            try
319            {
320                sig.update(bytes);
321            }
322            catch (SignatureException e)
323            {
324                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
325            }
326        }
327
328        public void write(int b)
329            throws IOException
330        {
331            try
332            {
333                sig.update((byte)b);
334            }
335            catch (SignatureException e)
336            {
337                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
338            }
339        }
340
341        boolean verify(byte[] expected)
342            throws SignatureException
343        {
344            return sig.verify(expected);
345        }
346    }
347}