1package org.bouncycastle.operator.jcajce;
2
3import java.io.IOException;
4import java.io.OutputStream;
5import java.security.GeneralSecurityException;
6import java.security.PrivateKey;
7import java.security.Provider;
8import java.security.SecureRandom;
9import java.security.Signature;
10import java.security.SignatureException;
11
12import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
13import org.bouncycastle.jcajce.DefaultJcaJceHelper;
14import org.bouncycastle.jcajce.NamedJcaJceHelper;
15import org.bouncycastle.jcajce.ProviderJcaJceHelper;
16import org.bouncycastle.operator.ContentSigner;
17import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
18import org.bouncycastle.operator.OperatorCreationException;
19import org.bouncycastle.operator.OperatorStreamException;
20import org.bouncycastle.operator.RuntimeOperatorException;
21
22public class JcaContentSignerBuilder
23{
24    private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
25    private SecureRandom random;
26    private String signatureAlgorithm;
27    private AlgorithmIdentifier sigAlgId;
28
29    public JcaContentSignerBuilder(String signatureAlgorithm)
30    {
31        this.signatureAlgorithm = signatureAlgorithm;
32        this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
33    }
34
35    public JcaContentSignerBuilder setProvider(Provider provider)
36    {
37        this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
38
39        return this;
40    }
41
42    public JcaContentSignerBuilder setProvider(String providerName)
43    {
44        this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
45
46        return this;
47    }
48
49    public JcaContentSignerBuilder setSecureRandom(SecureRandom random)
50    {
51        this.random = random;
52
53        return this;
54    }
55
56    public ContentSigner build(PrivateKey privateKey)
57        throws OperatorCreationException
58    {
59        try
60        {
61            final Signature sig = helper.createSignature(sigAlgId);
62
63            if (random != null)
64            {
65                sig.initSign(privateKey, random);
66            }
67            else
68            {
69                sig.initSign(privateKey);
70            }
71
72            return new ContentSigner()
73            {
74                private SignatureOutputStream stream = new SignatureOutputStream(sig);
75
76                public AlgorithmIdentifier getAlgorithmIdentifier()
77                {
78                    return sigAlgId;
79                }
80
81                public OutputStream getOutputStream()
82                {
83                    return stream;
84                }
85
86                public byte[] getSignature()
87                {
88                    try
89                    {
90                        return stream.getSignature();
91                    }
92                    catch (SignatureException e)
93                    {
94                        throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
95                    }
96                }
97            };
98        }
99        catch (GeneralSecurityException e)
100        {
101            throw new OperatorCreationException("cannot create signer: " + e.getMessage(), e);
102        }
103    }
104
105    private class SignatureOutputStream
106        extends OutputStream
107    {
108        private Signature sig;
109
110        SignatureOutputStream(Signature sig)
111        {
112            this.sig = sig;
113        }
114
115        public void write(byte[] bytes, int off, int len)
116            throws IOException
117        {
118            try
119            {
120                sig.update(bytes, off, len);
121            }
122            catch (SignatureException e)
123            {
124                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
125            }
126        }
127
128        public void write(byte[] bytes)
129            throws IOException
130        {
131            try
132            {
133                sig.update(bytes);
134            }
135            catch (SignatureException e)
136            {
137                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
138            }
139        }
140
141        public void write(int b)
142            throws IOException
143        {
144            try
145            {
146                sig.update((byte)b);
147            }
148            catch (SignatureException e)
149            {
150                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
151            }
152        }
153
154        byte[] getSignature()
155            throws SignatureException
156        {
157            return sig.sign();
158        }
159    }
160}
161