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