/
UtimacoJceSignatureContainer.java
225 lines (187 loc) · 7.76 KB
/
UtimacoJceSignatureContainer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package com.itextpdf.signingexamples.jce.utimaco;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.util.Arrays;
import java.util.Enumeration;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import com.itextpdf.io.source.ByteArrayOutputStream;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.signatures.IExternalSignatureContainer;
import CryptoServerJCE.CryptoServerProvider;
/**
* @author mkl
*/
public class UtimacoJceSignatureContainer implements IExternalSignatureContainer {
final PdfName subfilter;
/** The alias. */
String alias;
/** The private key object. */
PrivateKey pk;
/** The certificate chain. */
Certificate[] chain;
/** The algorithm identifier */
AlgorithmIdentifier algorithmIdentifier;
/** The BC content signer for the given key */
ContentSigner contentSigner;
/** The security provider */
final CryptoServerProvider provider;
public UtimacoJceSignatureContainer(CryptoServerProvider utimacoProvider, PdfName subfilter) {
this.subfilter = subfilter;
this.provider = utimacoProvider;
}
public UtimacoJceSignatureContainer select(String alias, char[] pin) throws GeneralSecurityException, IOException {
KeyStore ks = KeyStore.getInstance("CryptoServer", provider);
ks.load(null, pin);
boolean found = false;
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
String thisAlias = aliases.nextElement();
if (alias == null || alias.equals(thisAlias)) {
PrivateKey thisPk = (PrivateKey) ks.getKey(thisAlias, pin);
if (thisPk == null)
continue;
Certificate[] thisChain = ks.getCertificateChain(thisAlias);
if (thisChain == null)
continue;
found = true;
pk = thisPk;
chain = thisChain;
this.alias = thisAlias;
break;
}
}
if (!found) {
pk = null;
chain = null;
this.alias = null;
}
return this;
}
public UtimacoJceSignatureContainer with(String algorithm, AlgorithmParameterSpec paramSpec) {
if (paramSpec instanceof PSSParameterSpec) {
PSSParameterSpec pssSpec = (PSSParameterSpec)paramSpec;
algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSASSA_PSS, createPSSParams(pssSpec));
} else {
algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm);
}
contentSigner = new ContentSigner() {
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@Override
public byte[] getSignature() {
try {
Signature sig = Signature.getInstance(algorithm, provider);
sig.initSign(pk);
if (paramSpec != null)
sig.setParameter(paramSpec);
sig.update(outputStream.toByteArray());
return sig.sign();
} catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException)e;
throw new RuntimeException(e);
} finally {
outputStream.reset();
}
}
@Override
public OutputStream getOutputStream() {
return outputStream;
}
@Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return algorithmIdentifier;
}
};
return this;
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
try {
CMSTypedData msg = new CMSTypedDataInputStream(data);
X509CertificateHolder signCert = new X509CertificateHolder(chain[0].getEncoded());
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(contentSigner, signCert));
gen.addCertificates(new JcaCertStore(Arrays.asList(chain)));
CMSSignedData sigData = gen.generate(msg, false);
return sigData.getEncoded();
} catch (IOException | OperatorCreationException | CMSException e) {
throw new GeneralSecurityException(e);
}
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.put(PdfName.Filter, new PdfName("MKLx_GENERIC_SIGNER"));
signDic.put(PdfName.SubFilter, subfilter);
}
class CMSTypedDataInputStream implements CMSTypedData {
InputStream in;
public CMSTypedDataInputStream(InputStream is) {
in = is;
}
@Override
public ASN1ObjectIdentifier getContentType() {
return PKCSObjectIdentifiers.data;
}
@Override
public Object getContent() {
return in;
}
@Override
public void write(OutputStream out) throws IOException,
CMSException {
byte[] buffer = new byte[8 * 1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
}
}
private static RSASSAPSSparams createPSSParams(PSSParameterSpec pssSpec)
{
DigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
if (digId.getParameters() == null) {
digId = new AlgorithmIdentifier(digId.getAlgorithm(), DERNull.INSTANCE);
}
AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
if (mgfDig.getParameters() == null) {
mgfDig = new AlgorithmIdentifier(mgfDig.getAlgorithm(), DERNull.INSTANCE);
}
return new RSASSAPSSparams(
digId,
new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, mgfDig),
new ASN1Integer(pssSpec.getSaltLength()),
new ASN1Integer(pssSpec.getTrailerField()));
}
}