]> WPIA git - gigi.git/blob - src/org/cacert/gigi/Certificate.java
32ada17f59465b0c001fd5081d83886c0198a5b4
[gigi.git] / src / org / cacert / gigi / Certificate.java
1 package org.cacert.gigi;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.security.GeneralSecurityException;
9 import java.security.cert.CertificateFactory;
10 import java.security.cert.X509Certificate;
11 import java.sql.PreparedStatement;
12 import java.sql.ResultSet;
13 import java.sql.SQLException;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.LinkedList;
17 import java.util.List;
18
19 import org.cacert.gigi.database.DatabaseConnection;
20 import org.cacert.gigi.util.Job;
21 import org.cacert.gigi.util.Job.JobType;
22 import org.cacert.gigi.util.KeyStorage;
23 import org.cacert.gigi.util.Notary;
24
25 public class Certificate {
26
27     public enum SANType {
28         EMAIL("email"), DNS("DNS");
29
30         private final String opensslName;
31
32         private SANType(String opensslName) {
33             this.opensslName = opensslName;
34         }
35
36         public String getOpensslName() {
37             return opensslName;
38         }
39     }
40
41     public static class SubjectAlternateName {
42
43         private SANType type;
44
45         private String name;
46
47         public SubjectAlternateName(SANType type, String name) {
48             this.type = type;
49             this.name = name;
50         }
51
52         public String getName() {
53             return name;
54         }
55
56         public SANType getType() {
57             return type;
58         }
59
60     }
61
62     public enum CSRType {
63         CSR, SPKAC;
64     }
65
66     private int id;
67
68     private int ownerId;
69
70     private String serial;
71
72     private String dn;
73
74     private String md;
75
76     private String csrName;
77
78     private String crtName;
79
80     private String csr = null;
81
82     private CSRType csrType;
83
84     private List<SubjectAlternateName> sans;
85
86     public Certificate(int ownerId, String dn, String md, String csr, CSRType csrType, SubjectAlternateName... sans) {
87         this.ownerId = ownerId;
88         this.dn = dn;
89         this.md = md;
90         this.csr = csr;
91         this.csrType = csrType;
92         this.sans = Arrays.asList(sans);
93     }
94
95     private Certificate(String serial) {
96         try {
97             PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id,subject, md, csr_name, crt_name,memid FROM `emailcerts` WHERE serial=?");
98             ps.setString(1, serial);
99             ResultSet rs = ps.executeQuery();
100             if ( !rs.next()) {
101                 throw new IllegalArgumentException("Invalid mid " + serial);
102             }
103             this.id = rs.getInt(1);
104             dn = rs.getString(2);
105             md = rs.getString(3);
106             csrName = rs.getString(4);
107             crtName = rs.getString(5);
108             ownerId = rs.getInt(6);
109             this.serial = serial;
110
111             PreparedStatement ps2 = DatabaseConnection.getInstance().prepare("SELECT contents, type FROM `subjectAlternativeNames` WHERE certId=?");
112             ps2.setInt(1, id);
113             ResultSet rs2 = ps2.executeQuery();
114             sans = new LinkedList<>();
115             while (rs2.next()) {
116                 sans.add(new SubjectAlternateName(SANType.valueOf(rs2.getString("type").toUpperCase()), rs2.getString("contents")));
117             }
118             rs2.close();
119
120             rs.close();
121         } catch (SQLException e) {
122             e.printStackTrace();
123         }
124     }
125
126     public enum CertificateStatus {
127         /**
128          * This certificate is not in the database, has no id and only exists as
129          * this java object.
130          */
131         DRAFT(),
132         /**
133          * The certificate has been signed. It is stored in the database.
134          * {@link Certificate#cert()} is valid.
135          */
136         ISSUED(),
137
138         /**
139          * The certificate has been revoked.
140          */
141         REVOKED(),
142
143         /**
144          * If this certificate cannot be updated because an error happened in
145          * the signer.
146          */
147         ERROR();
148
149         private CertificateStatus() {}
150
151     }
152
153     public CertificateStatus getStatus() throws SQLException {
154         if (id == 0) {
155             return CertificateStatus.DRAFT;
156         }
157         PreparedStatement searcher = DatabaseConnection.getInstance().prepare("SELECT crt_name, created, revoked, serial FROM emailcerts WHERE id=?");
158         searcher.setInt(1, id);
159         ResultSet rs = searcher.executeQuery();
160         if ( !rs.next()) {
161             throw new IllegalStateException("Certificate not in Database");
162         }
163
164         crtName = rs.getString(1);
165         serial = rs.getString(4);
166         if (rs.getTime(2) == null) {
167             return CertificateStatus.DRAFT;
168         }
169         if (rs.getTime(2) != null && rs.getTime(3) == null) {
170             return CertificateStatus.ISSUED;
171         }
172         return CertificateStatus.REVOKED;
173     }
174
175     public Job issue() throws IOException, SQLException {
176         if (getStatus() != CertificateStatus.DRAFT) {
177             throw new IllegalStateException();
178         }
179         Notary.writeUserAgreement(ownerId, "CCA", "issue certificate", "", true, 0);
180
181         PreparedStatement inserter = DatabaseConnection.getInstance().prepare("INSERT INTO emailcerts SET md=?, subject=?, csr_type=?, crt_name='', memid=?, profile=1");
182         inserter.setString(1, md);
183         inserter.setString(2, dn);
184         inserter.setString(3, csrType.toString());
185         inserter.setInt(4, ownerId);
186         inserter.execute();
187         id = DatabaseConnection.lastInsertId(inserter);
188         File csrFile = KeyStorage.locateCsr(id);
189         csrName = csrFile.getPath();
190         FileOutputStream fos = new FileOutputStream(csrFile);
191         fos.write(csr.getBytes());
192         fos.close();
193
194         // TODO draft to insert SANs
195         PreparedStatement san = DatabaseConnection.getInstance().prepare("INSERT INTO subjectAlternativeNames SET certId=?, contents=?, type=?");
196         for (SubjectAlternateName subjectAlternateName : sans) {
197             san.setInt(1, id);
198             san.setString(2, subjectAlternateName.getName());
199             san.setString(3, subjectAlternateName.getType().getOpensslName());
200             san.execute();
201         }
202
203         PreparedStatement updater = DatabaseConnection.getInstance().prepare("UPDATE emailcerts SET csr_name=? WHERE id=?");
204         updater.setString(1, csrName);
205         updater.setInt(2, id);
206         updater.execute();
207         return Job.submit(this, JobType.SIGN);
208
209     }
210
211     public Job revoke() throws SQLException {
212         if (getStatus() != CertificateStatus.ISSUED) {
213             throw new IllegalStateException();
214         }
215         return Job.submit(this, JobType.REVOKE);
216
217     }
218
219     public X509Certificate cert() throws IOException, GeneralSecurityException, SQLException {
220         CertificateStatus status = getStatus();
221         if (status != CertificateStatus.ISSUED) {
222             throw new IllegalStateException(status + " is not wanted here.");
223         }
224         InputStream is = null;
225         X509Certificate crt = null;
226         try {
227             is = new FileInputStream(crtName);
228             CertificateFactory cf = CertificateFactory.getInstance("X.509");
229             crt = (X509Certificate) cf.generateCertificate(is);
230         } finally {
231             if (is != null) {
232                 is.close();
233             }
234         }
235         return crt;
236     }
237
238     public Certificate renew() {
239         return null;
240     }
241
242     public int getId() {
243         return id;
244     }
245
246     public String getSerial() {
247         try {
248             getStatus();
249         } catch (SQLException e) {
250             e.printStackTrace();
251         } // poll changes
252         return serial;
253     }
254
255     public String getDistinguishedName() {
256         return dn;
257     }
258
259     public String getMessageDigest() {
260         return md;
261     }
262
263     public int getOwnerId() {
264         return ownerId;
265     }
266
267     public List<SubjectAlternateName> getSans() {
268         return Collections.unmodifiableList(sans);
269     }
270
271     public static Certificate getBySerial(String serial) {
272         // TODO caching?
273         try {
274             return new Certificate(serial);
275         } catch (IllegalArgumentException e) {
276
277         }
278         return null;
279     }
280
281 }