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 String hostname;
76
77
78 private String[] dnsSubjectAltNames;
79
80
81 private String[] uriSubjectAltNames;
82
83
84 private File privateKeyFile;
85
86
87 private File certificateFile;
88
89
90 private File keystoreFile;
91
92
93 private String keystorePassword;
94
95
96 public void execute() throws BuildException {
97 validate();
98 KeyPair keypair = generateKeyPair();
99 X509Certificate certificate = generateCertificate(keypair);
100
101 if (privateKeyFile != null) {
102 try {
103 privateKeyFile.createNewFile();
104 PEMWriter keyOut = new PEMWriter(new FileWriter(privateKeyFile));
105 keyOut.writeObject(keypair.getPrivate());
106 keyOut.flush();
107 keyOut.close();
108 } catch (Exception e) {
109 throw new BuildException("Unable to create private key file.", e);
110 }
111 }
112
113 if (certificateFile != null) {
114 try {
115 certificateFile.createNewFile();
116 PEMWriter certOut = new PEMWriter(new FileWriter(certificateFile));
117 certOut.writeObject(certificate);
118 certOut.flush();
119 certOut.close();
120 } catch (Exception e) {
121 throw new BuildException("Unable to create private key file.", e);
122 }
123 }
124
125 if (keystoreFile != null) {
126 try {
127 KeyStore store = KeyStore.getInstance("JKS");
128 store.load(null, null);
129 store.setKeyEntry(hostname, keypair.getPrivate(), keystorePassword.toCharArray(),
130 new X509Certificate[] { certificate });
131
132 FileOutputStream keystoreOut = new FileOutputStream(keystoreFile);
133 store.store(keystoreOut, keystorePassword.toCharArray());
134 keystoreOut.flush();
135 keystoreOut.close();
136 } catch (Exception e) {
137 throw new BuildException(e);
138 }
139 }
140 }
141
142
143
144
145
146
147 public void setKeyType(KeyType type) {
148 keyType = type.getValue();
149 }
150
151
152
153
154
155
156 public void setKeysize(int size) {
157 keysize = size;
158 }
159
160
161
162
163
164
165 public void setHostName(String name) {
166 hostname = name;
167 }
168
169
170
171
172
173
174 public void setPrivateKeyFile(File file) {
175 privateKeyFile = file;
176 }
177
178
179
180
181
182
183 public void setCertificateFile(File file) {
184 certificateFile = file;
185 }
186
187
188
189
190
191
192 public void setKeystoreFile(File file) {
193 keystoreFile = file;
194 }
195
196
197
198
199
200
201 public void setKeystorePassword(String password) {
202 keystorePassword = password;
203 }
204
205
206
207
208
209
210 public void setDnsSubjectAltNames(String altNames){
211 dnsSubjectAltNames = altNames.split(" ");
212 }
213
214
215
216
217
218
219 public void setUriSubjectAltNames(String altNames){
220 uriSubjectAltNames = altNames.split(" ");
221 }
222
223
224 protected void validate() throws BuildException {
225 if (keysize > 2048) {
226 log("Key size is greater than 2048, this may cause problems with some JVMs", Project.MSG_WARN);
227 }
228
229 if (hostname == null || hostname.length() == 0) {
230 throw new BuildException("The hostname attribute is required and may not contain an empty value");
231 }
232
233 if (keystoreFile != null && (keystorePassword == null || keystorePassword.length() == 0)) {
234 throw new BuildException("Keystore password may not be null if a keystore file is given");
235 }
236 }
237
238
239
240
241
242
243
244
245 protected KeyPair generateKeyPair() throws BuildException {
246 try {
247 KeyPairGenerator generator = KeyPairGenerator.getInstance(keyType);
248 generator.initialize(keysize);
249 return generator.generateKeyPair();
250 } catch (NoSuchAlgorithmException e) {
251 throw new BuildException("The " + keyType + " key type is not supported by this JVM");
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264 protected X509Certificate generateCertificate(KeyPair keypair) throws BuildException {
265 try {
266 X509V3CertificateGenerator certifcateGenerator = new X509V3CertificateGenerator();
267 certifcateGenerator.setPublicKey(keypair.getPublic());
268
269 StringBuffer dnBuffer = new StringBuffer("CN=").append(hostname);
270
271 X509Name dn = new X509Name(false, dnBuffer.toString(), new RdnConverter());
272 certifcateGenerator.setIssuerDN(dn);
273 certifcateGenerator.setSubjectDN(dn);
274
275 GregorianCalendar date = new GregorianCalendar();
276 certifcateGenerator.setNotBefore(date.getTime());
277
278 date.set(GregorianCalendar.YEAR, date.get(GregorianCalendar.YEAR) + 20);
279 certifcateGenerator.setNotAfter(date.getTime());
280
281 certifcateGenerator.setSerialNumber(new BigInteger(160, new SecureRandom()));
282
283 certifcateGenerator.setSignatureAlgorithm("SHA1withRSA");
284
285 certifcateGenerator.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames(
286 new DERSequence(buildSubjectAltNames())));
287
288 certifcateGenerator.addExtension(X509Extensions.SubjectKeyIdentifier, false,
289 new SubjectKeyIdentifierStructure(keypair.getPublic()));
290
291 return certifcateGenerator.generate(keypair.getPrivate());
292 } catch (Exception e) {
293 log(e.toString(), Project.MSG_ERR);
294 throw new BuildException("Unable to generate self-signed certificate", e);
295 }
296 }
297
298
299
300
301
302
303 protected ASN1Encodable[] buildSubjectAltNames(){
304 ArrayList<ASN1Encodable> subjectAltNames = new ArrayList<ASN1Encodable>();
305
306 subjectAltNames.add(new GeneralName(GeneralName.dNSName, hostname));
307
308 if(dnsSubjectAltNames != null){
309 for(String subjectAltName : dnsSubjectAltNames){
310 subjectAltNames.add(new GeneralName(GeneralName.dNSName, subjectAltName));
311 }
312 }
313
314 if(uriSubjectAltNames != null){
315 for(String subjectAltName : uriSubjectAltNames){
316 subjectAltNames.add(new GeneralName(GeneralName.uniformResourceIdentifier, subjectAltName));
317 }
318 }
319
320 return subjectAltNames.toArray(new ASN1Encodable[0]);
321 }
322
323
324 public static class KeyType extends EnumeratedAttribute {
325
326 public String[] getValues() {
327 return new String[] { "DSA", "RSA" };
328 }
329 }
330 }