]> WPIA git - gigi.git/blob - util-testing/org/cacert/gigi/util/SimpleSigner.java
fix: several testcases to the new configuration/structure
[gigi.git] / util-testing / org / cacert / gigi / util / SimpleSigner.java
1 package org.cacert.gigi.util;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.OutputStreamWriter;
11 import java.io.PrintWriter;
12 import java.io.Reader;
13 import java.math.BigInteger;
14 import java.security.GeneralSecurityException;
15 import java.security.cert.CertificateFactory;
16 import java.security.cert.X509Certificate;
17 import java.sql.SQLException;
18 import java.sql.Timestamp;
19 import java.text.ParseException;
20 import java.text.SimpleDateFormat;
21 import java.util.Calendar;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.Properties;
25 import java.util.TimeZone;
26
27 import org.cacert.gigi.database.DatabaseConnection;
28 import org.cacert.gigi.database.GigiPreparedStatement;
29 import org.cacert.gigi.database.GigiResultSet;
30 import org.cacert.gigi.dbObjects.Certificate;
31 import org.cacert.gigi.dbObjects.Certificate.CSRType;
32 import org.cacert.gigi.output.DateSelector;
33
34 public class SimpleSigner {
35
36     private static GigiPreparedStatement warnMail;
37
38     private static GigiPreparedStatement updateMail;
39
40     private static GigiPreparedStatement readyCerts;
41
42     private static GigiPreparedStatement getSANSs;
43
44     private static GigiPreparedStatement revoke;
45
46     private static GigiPreparedStatement revokeCompleted;
47
48     private static GigiPreparedStatement finishJob;
49
50     private static volatile boolean running = true;
51
52     private static Thread runner;
53
54     private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss'Z'");
55
56     static {
57         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
58         sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
59     }
60
61     public static void main(String[] args) throws IOException, SQLException, InterruptedException {
62         Properties p = new Properties();
63         try (Reader reader = new InputStreamReader(new FileInputStream("config/gigi.properties"), "UTF-8")) {
64             p.load(reader);
65         }
66         DatabaseConnection.init(p);
67
68         runSigner();
69     }
70
71     public static void stopSigner() throws InterruptedException {
72         Thread capturedRunner;
73         synchronized (SimpleSigner.class) {
74             if (runner == null) {
75                 throw new IllegalStateException("already stopped");
76             }
77             capturedRunner = runner;
78             running = false;
79             SimpleSigner.class.notifyAll();
80         }
81         capturedRunner.join();
82     }
83
84     public synchronized static void runSigner() throws SQLException, IOException, InterruptedException {
85         if (runner != null) {
86             throw new IllegalStateException("already running");
87         }
88         running = true;
89         readyCerts = DatabaseConnection.getInstance().prepare("SELECT certs.id AS id, certs.csr_name, jobs.id AS jobid, csr_type, md, executeFrom, executeTo, profile FROM jobs " + //
90                 "INNER JOIN certs ON certs.id=jobs.targetId " + //
91                 "INNER JOIN profiles ON profiles.id=certs.profile " + //
92                 "WHERE jobs.state='open' "//
93                 + "AND task='sign'");
94
95         getSANSs = DatabaseConnection.getInstance().prepare("SELECT contents, type FROM subjectAlternativeNames " + //
96                 "WHERE certId=?");
97
98         updateMail = DatabaseConnection.getInstance().prepare("UPDATE certs SET crt_name=?," + " created=NOW(), serial=?, caid=1 WHERE id=?");
99         warnMail = DatabaseConnection.getInstance().prepare("UPDATE jobs SET warning=warning+1, state=IF(warning<3, 'open','error') WHERE id=?");
100
101         revoke = DatabaseConnection.getInstance().prepare("SELECT certs.id, certs.csr_name,jobs.id FROM jobs INNER JOIN certs ON jobs.targetId=certs.id" + " WHERE jobs.state='open' AND task='revoke'");
102         revokeCompleted = DatabaseConnection.getInstance().prepare("UPDATE certs SET revoked=NOW() WHERE id=?");
103
104         finishJob = DatabaseConnection.getInstance().prepare("UPDATE jobs SET state='done' WHERE id=?");
105
106         runner = new Thread() {
107
108             @Override
109             public void run() {
110                 work();
111             }
112
113         };
114         runner.start();
115     }
116
117     private synchronized static void work() {
118         try {
119             gencrl();
120         } catch (IOException e2) {
121             e2.printStackTrace();
122         } catch (InterruptedException e2) {
123             e2.printStackTrace();
124         }
125
126         while (running) {
127             try {
128                 signCertificates();
129                 revokeCertificates();
130
131                 SimpleSigner.class.wait(5000);
132             } catch (IOException e) {
133                 e.printStackTrace();
134             } catch (SQLException e) {
135                 e.printStackTrace();
136             } catch (InterruptedException e1) {
137             }
138         }
139         runner = null;
140     }
141
142     private static void revokeCertificates() throws SQLException, IOException, InterruptedException {
143         GigiResultSet rs = revoke.executeQuery();
144         boolean worked = false;
145         while (rs.next()) {
146             int id = rs.getInt(1);
147             File crt = KeyStorage.locateCrt(id);
148             String[] call = new String[] {
149                     "openssl", "ca",//
150                     "-cert",
151                     "../unassured.crt",//
152                     "-keyfile",
153                     "../unassured.key",//
154                     "-revoke",
155                     "../../" + crt.getPath(),//
156                     "-batch",//
157                     "-config",
158                     "../selfsign.config"
159
160             };
161             Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
162             System.out.println("revoking: " + crt.getPath());
163             if (p1.waitFor() == 0) {
164                 worked = true;
165                 revokeCompleted.setInt(1, id);
166                 revokeCompleted.execute();
167                 finishJob.setInt(1, rs.getInt(3));
168                 finishJob.execute();
169             } else {
170                 System.out.println("Failed");
171             }
172         }
173         if (worked) {
174             gencrl();
175         }
176     }
177
178     private static void gencrl() throws IOException, InterruptedException {
179         String[] call = new String[] {
180                 "openssl", "ca",//
181                 "-cert",
182                 "../unassured.crt",//
183                 "-keyfile",
184                 "../unassured.key",//
185                 "-gencrl",//
186                 "-crlhours",//
187                 "12",//
188                 "-out",
189                 "../unassured.crl",//
190                 "-config",
191                 "../selfsign.config"
192
193         };
194         Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
195         if (p1.waitFor() != 0) {
196             System.out.println("Error while generating crl.");
197         }
198     }
199
200     private static int counter = 0;
201
202     private static void signCertificates() throws SQLException {
203         System.out.println("Checking...");
204         GigiResultSet rs = readyCerts.executeQuery();
205
206         Calendar c = Calendar.getInstance();
207         c.setTimeZone(TimeZone.getTimeZone("UTC"));
208
209         while (rs.next()) {
210             System.out.println("Task");
211             String csrname = rs.getString("csr_name");
212             int id = rs.getInt("id");
213             System.out.println("sign: " + csrname);
214             try {
215                 String csrType = rs.getString("csr_type");
216                 CSRType ct = CSRType.valueOf(csrType);
217                 File crt = KeyStorage.locateCrt(id);
218
219                 Timestamp from = rs.getTimestamp("executeFrom");
220                 String length = rs.getString("executeTo");
221                 Date fromDate;
222                 Date toDate;
223                 if (from == null) {
224                     fromDate = new Date(System.currentTimeMillis());
225                 } else {
226                     fromDate = new Date(from.getTime());
227                 }
228                 if (length.endsWith("m") || length.endsWith("y")) {
229                     String num = length.substring(0, length.length() - 1);
230                     int inter = Integer.parseInt(num);
231                     c.setTime(fromDate);
232                     if (length.endsWith("m")) {
233                         c.add(Calendar.MONTH, inter);
234                     } else {
235                         c.add(Calendar.YEAR, inter);
236                     }
237                     toDate = c.getTime();
238                 } else {
239                     toDate = DateSelector.getDateFormat().parse(length);
240                 }
241
242                 getSANSs.setInt(1, id);
243                 GigiResultSet san = getSANSs.executeQuery();
244
245                 File f = new File("keys", "SANFile" + System.currentTimeMillis() + (counter++) + ".cfg");
246                 PrintWriter cfg = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
247                 boolean first = true;
248                 while (san.next()) {
249                     if ( !first) {
250                         cfg.print(", ");
251                     } else {
252                         cfg.print("subjectAltName=");
253                     }
254                     first = false;
255                     cfg.print(san.getString("type"));
256                     cfg.print(":");
257                     cfg.print(san.getString("contents"));
258                 }
259                 cfg.println();
260                 // TODO look them up!
261                 cfg.println("keyUsage=critical," + "digitalSignature, keyEncipherment, keyAgreement");
262                 cfg.println("extendedKeyUsage=critical," + "clientAuth");
263                 cfg.close();
264
265                 int profile = rs.getInt("profile");
266                 String ca = "unassured";
267                 if (profile == 1) {
268                     ca = "unassured";
269                 } else if (profile != 1) {
270                     ca = "assured";
271                 }
272                 HashMap<String, String> subj = new HashMap<>();
273                 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT name, value FROM certAvas WHERE certId=?");
274                 ps.setInt(1, rs.getInt("id"));
275                 GigiResultSet rs2 = ps.executeQuery();
276                 while (rs2.next()) {
277                     String name = rs2.getString("name");
278                     if (name.equals("EMAIL")) {
279                         name = "emailAddress";
280                     }
281                     subj.put(name, rs2.getString("value"));
282                 }
283                 if (subj.size() == 0) {
284                     subj.put("CN", "<empty>");
285                     System.out.println("WARNING: DN was empty");
286                 }
287                 System.out.println(subj);
288                 String[] call;
289                 synchronized (sdf) {
290                     call = new String[] {
291                             "openssl", "ca",//
292                             "-in",
293                             "../../" + csrname,//
294                             "-cert",
295                             "../" + ca + ".crt",//
296                             "-keyfile",
297                             "../" + ca + ".key",//
298                             "-out",
299                             "../../" + crt.getPath(),//
300                             "-utf8",
301                             "-startdate",
302                             sdf.format(fromDate),//
303                             "-enddate",
304                             sdf.format(toDate),//
305                             "-batch",//
306                             "-md",
307                             rs.getString("md"),//
308                             "-extfile",
309                             "../" + f.getName(),//
310
311                             "-subj",
312                             Certificate.stringifyDN(subj),//
313                             "-config",
314                             "../selfsign.config"//
315                     };
316                     for (String string : call) {
317                         System.out.print(" " + string);
318                     }
319                     System.out.println();
320                 }
321
322                 if (ct == CSRType.SPKAC) {
323                     call[2] = "-spkac";
324                 }
325
326                 Process p1 = Runtime.getRuntime().exec(call, null, new File("keys/unassured.ca"));
327
328                 int waitFor = p1.waitFor();
329                 /*
330                  * if ( !f.delete()) {
331                  * System.err.println("Could not delete SAN-File " +
332                  * f.getAbsolutePath()); }
333                  */
334                 if (waitFor == 0) {
335                     try (InputStream is = new FileInputStream(crt)) {
336                         CertificateFactory cf = CertificateFactory.getInstance("X.509");
337                         X509Certificate crtp = (X509Certificate) cf.generateCertificate(is);
338                         BigInteger serial = crtp.getSerialNumber();
339                         updateMail.setString(1, crt.getPath());
340                         updateMail.setString(2, serial.toString(16));
341                         updateMail.setInt(3, id);
342                         updateMail.execute();
343
344                         finishJob.setInt(1, rs.getInt("jobid"));
345                         finishJob.execute();
346                         System.out.println("signed: " + id);
347                         continue;
348                     }
349                 } else {
350                     BufferedReader br = new BufferedReader(new InputStreamReader(p1.getErrorStream(), "UTF-8"));
351                     String s;
352                     while ((s = br.readLine()) != null) {
353                         System.out.println(s);
354                     }
355                 }
356             } catch (GeneralSecurityException e) {
357                 e.printStackTrace();
358             } catch (IOException e) {
359                 e.printStackTrace();
360             } catch (ParseException e) {
361                 e.printStackTrace();
362             } catch (InterruptedException e1) {
363                 e1.printStackTrace();
364             }
365             System.out.println("Error with: " + id);
366             warnMail.setInt(1, rs.getInt("jobid"));
367             warnMail.execute();
368
369         }
370         rs.close();
371     }
372 }