1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.ant.pki;
18
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.FileWriter;
22 import java.math.BigInteger;
23 import java.security.KeyPair;
24 import java.security.KeyPairGenerator;
25 import java.security.KeyStore;
26 import java.security.NoSuchAlgorithmException;
27 import java.security.SecureRandom;
28 import java.security.cert.X509Certificate;
29 import java.util.ArrayList;
30 import java.util.GregorianCalendar;
31
32 import org.apache.tools.ant.BuildException;
33 import org.apache.tools.ant.Project;
34 import org.apache.tools.ant.Task;
35 import org.apache.tools.ant.types.EnumeratedAttribute;
36 import org.bouncycastle.asn1.ASN1Encodable;
37 import org.bouncycastle.asn1.DERSequence;
38 import org.bouncycastle.asn1.x509.GeneralName;
39 import org.bouncycastle.asn1.x509.GeneralNames;
40 import org.bouncycastle.asn1.x509.X509Extensions;
41 import org.bouncycastle.asn1.x509.X509Name;
42 import org.bouncycastle.openssl.PEMWriter;
43 import org.bouncycastle.x509.X509V3CertificateGenerator;
44 import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class SelfSignedCertificate extends Task {
67
68
69 private String keyType = "RSA";
70
71
72 private int keysize = 2048;
73
74
75 private int certificateLifetime = 20;
76
77
78 private String hostname;
79
80
81 private String[] dnsSubjectAltNames;
82
83
84 private String[] uriSubjectAltNames;
85
86
87 private File privateKeyFile;
88
89
90 private File certificateFile;
91
92
93 private File keystoreFile;
94
95
96 private String keystorePassword;
97
98
99 public void execute() throws BuildException {
100 validate();
101 KeyPair keypair = generateKeyPair();
102 X509Certificate certificate = generateCertificate(keypair);
103
104 if (privateKeyFile != null) {
105 try {
106 privateKeyFile.createNewFile();
107 PEMWriter keyOut = new PEMWriter(new FileWriter(privateKeyFile));
108 keyOut.writeObject(keypair.getPrivate());
109 keyOut.flush();
110 keyOut.close();
111 } catch (Exception e) {
112 throw new BuildException("Unable to create private key file.", e);
113 }
114 }
115
116 if (certificateFile != null) {
117 try {
118 certificateFile.createNewFile();
119 PEMWriter certOut = new PEMWriter(new FileWriter(certificateFile));
120 certOut.writeObject(certificate);
121 certOut.flush();
122 certOut.close();
123 } catch (Exception e) {
124 throw new BuildException("Unable to create private key file.", e);
125 }
126 }
127
128 if (keystoreFile != null) {
129 try {
130 KeyStore store = KeyStore.getInstance("JKS");
131 store.load(null, null);
132 store.setKeyEntry(hostname, keypair.getPrivate(), keystorePassword.toCharArray(),
133 new X509Certificate[] { certificate });
134
135 FileOutputStream keystoreOut = new FileOutputStream(keystoreFile);
136 store.store(keystoreOut, keystorePassword.toCharArray());
137 keystoreOut.flush();
138 keystoreOut.close();
139 } catch (Exception e) {
140 throw new BuildException(e);
141 }
142 }
143 }
144
145
146
147
148
149
150 public void setKeyType(KeyType type) {
151 keyType = type.getValue();
152 }
153
154
155
156
157
158
159 public void setKeysize(int size) {
160 keysize = size;
161 }
162
163
164
165
166
167
168 public void setCertificateLifetime(int lifetime) {
169 certificateLifetime = lifetime;
170 }
171
172
173
174
175
176
177 public void setHostName(String name) {
178 hostname = name;
179 }
180
181
182
183
184
185
186 public void setPrivateKeyFile(File file) {
187 privateKeyFile = file;
188 }
189
190
191
192
193
194
195 public void setCertificateFile(File file) {
196 certificateFile = file;
197 }
198
199
200
201
202
203
204 public void setKeystoreFile(File file) {
205 keystoreFile = file;
206 }
207
208
209
210
211
212
213 public void setKeystorePassword(String password) {
214 keystorePassword = password;
215 }
216
217
218
219
220
221
222 public void setDnsSubjectAltNames(String altNames){
223 dnsSubjectAltNames = altNames.split(" ");
224 }
225
226
227
228
229
230
231 public void setUriSubjectAltNames(String altNames){
232 uriSubjectAltNames = altNames.split(" ");
233 }
234
235
236 protected void validate() throws BuildException {
237 if (keysize > 2048) {
238 log("Key size is greater than 2048, this may cause problems with some JVMs", Project.MSG_WARN);
239 }
240
241 if (hostname == null || hostname.length() == 0) {
242 throw new BuildException("The hostname attribute is required and may not contain an empty value");
243 }
244
245 if (keystoreFile != null && (keystorePassword == null || keystorePassword.length() == 0)) {
246 throw new BuildException("Keystore password may not be null if a keystore file is given");
247 }
248 }
249
250
251
252
253
254
255
256
257 protected KeyPair generateKeyPair() throws BuildException {
258 try {
259 KeyPairGenerator generator = KeyPairGenerator.getInstance(keyType);
260 generator.initialize(keysize);
261 return generator.generateKeyPair();
262 } catch (NoSuchAlgorithmException e) {
263 throw new BuildException("The " + keyType + " key type is not supported by this JVM");
264 }
265 }
266
267
268
269
270
271
272
273
274
275
276 protected X509Certificate generateCertificate(KeyPair keypair) throws BuildException {
277 try {
278 X509V3CertificateGenerator certifcateGenerator = new X509V3CertificateGenerator();
279 certifcateGenerator.setPublicKey(keypair.getPublic());
280
281 StringBuffer dnBuffer = new StringBuffer("CN=").append(hostname);
282
283 X509Name dn = new X509Name(false, dnBuffer.toString(), new RdnConverter());
284 certifcateGenerator.setIssuerDN(dn);
285 certifcateGenerator.setSubjectDN(dn);
286
287 GregorianCalendar date = new GregorianCalendar();
288 certifcateGenerator.setNotBefore(date.getTime());
289
290 date.set(GregorianCalendar.YEAR, date.get(GregorianCalendar.YEAR) + certificateLifetime);
291 certifcateGenerator.setNotAfter(date.getTime());
292
293 certifcateGenerator.setSerialNumber(new BigInteger(160, new SecureRandom()));
294
295 certifcateGenerator.setSignatureAlgorithm("SHA1withRSA");
296
297 certifcateGenerator.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames(
298 new DERSequence(buildSubjectAltNames())));
299
300 certifcateGenerator.addExtension(X509Extensions.SubjectKeyIdentifier, false,
301 new SubjectKeyIdentifierStructure(keypair.getPublic()));
302
303 return certifcateGenerator.generate(keypair.getPrivate());
304 } catch (Exception e) {
305 log(e.toString(), Project.MSG_ERR);
306 throw new BuildException("Unable to generate self-signed certificate", e);
307 }
308 }
309
310
311
312
313
314
315 protected ASN1Encodable[] buildSubjectAltNames(){
316 ArrayList<ASN1Encodable> subjectAltNames = new ArrayList<ASN1Encodable>();
317
318 subjectAltNames.add(new GeneralName(GeneralName.dNSName, hostname));
319
320 if(dnsSubjectAltNames != null){
321 for(String subjectAltName : dnsSubjectAltNames){
322 subjectAltNames.add(new GeneralName(GeneralName.dNSName, subjectAltName));
323 }
324 }
325
326 if(uriSubjectAltNames != null){
327 for(String subjectAltName : uriSubjectAltNames){
328 subjectAltNames.add(new GeneralName(GeneralName.uniformResourceIdentifier, subjectAltName));
329 }
330 }
331
332 return subjectAltNames.toArray(new ASN1Encodable[0]);
333 }
334
335
336 public static class KeyType extends EnumeratedAttribute {
337
338 public String[] getValues() {
339 return new String[] { "DSA", "RSA" };
340 }
341 }
342 }