]> WPIA git - gigi.git/blob - src/org/cacert/gigi/Certificate.java
2df5e395e5e1878567cc2d0d737fbc1890e7a09f
[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
15 import org.cacert.gigi.database.DatabaseConnection;
16 import org.cacert.gigi.util.KeyStorage;
17 import org.cacert.gigi.util.Notary;
18
19 public class Certificate {
20         private int id;
21         private int ownerId;
22         private String serial;
23         private String dn;
24         private String md;
25         private String csrName;
26         private String crtName;
27         private String csr = null;
28
29         public Certificate(int ownerId, String dn, String md, String csr) {
30                 this.ownerId = ownerId;
31                 this.dn = dn;
32                 this.md = md;
33                 this.csr = csr;
34         }
35
36         private Certificate(String serial) {
37                 try {
38                         PreparedStatement ps = DatabaseConnection.getInstance().prepare(
39                                 "SELECT id,subject, md, csr_name, crt_name,memid FROM `emailcerts` WHERE serial=?");
40                         ps.setString(1, serial);
41                         ResultSet rs = ps.executeQuery();
42                         if (!rs.next()) {
43                                 throw new IllegalArgumentException("Invalid mid " + serial);
44                         }
45                         this.id = rs.getInt(1);
46                         dn = rs.getString(2);
47                         md = rs.getString(3);
48                         csrName = rs.getString(4);
49                         crtName = rs.getString(5);
50                         ownerId = rs.getInt(6);
51                         this.serial = serial;
52                         rs.close();
53                 } catch (SQLException e) {
54                         e.printStackTrace();
55                 }
56         }
57
58         public enum CertificateStatus {
59                 /**
60                  * This certificate is not in the database, has no id and only exists as
61                  * this java object.
62                  */
63                 DRAFT(false),
64                 /**
65                  * The certificate has been written to the database and is waiting for
66                  * the signer to sign it.
67                  */
68                 SIGNING(true),
69                 /**
70                  * The certificate has been signed. It is stored in the database.
71                  * {@link Certificate#cert()} is valid.
72                  */
73                 ISSUED(false),
74                 /**
75                  * The cetrificate is about to be revoked by the signer bot.
76                  */
77                 BEING_REVOKED(true),
78
79                 /**
80                  * The certificate has been revoked.
81                  */
82                 REVOKED(false),
83
84                 /**
85                  * If this certificate cannot be updated because an error happened in
86                  * the signer.
87                  */
88                 ERROR(false);
89
90                 private boolean unstable;
91
92                 private CertificateStatus(boolean unstable) {
93                         this.unstable = unstable;
94                 }
95
96                 /**
97                  * Checks, iff this certificate stage will be left by signer actions.
98                  * 
99                  * @return True, iff this certificate stage will be left by signer
100                  *         actions.
101                  */
102                 public boolean isUnstable() {
103                         return unstable;
104                 }
105
106         }
107
108         public CertificateStatus getStatus() throws SQLException {
109                 if (id == 0) {
110                         return CertificateStatus.DRAFT;
111                 }
112                 PreparedStatement searcher = DatabaseConnection.getInstance().prepare(
113                         "SELECT crt_name, created, revoked, warning, serial FROM emailcerts WHERE id=?");
114                 searcher.setInt(1, id);
115                 ResultSet rs = searcher.executeQuery();
116                 if (!rs.next()) {
117                         throw new IllegalStateException("Certificate not in Database");
118                 }
119                 if (rs.getInt(4) >= 3) {
120                         return CertificateStatus.ERROR;
121                 }
122
123                 if (rs.getString(2) == null) {
124                         return CertificateStatus.SIGNING;
125                 }
126                 crtName = rs.getString(1);
127                 serial = rs.getString(5);
128                 if (rs.getTime(2) != null && rs.getTime(3) == null) {
129                         return CertificateStatus.ISSUED;
130                 }
131                 if (rs.getTime(2) != null && rs.getString(3).equals("1970-01-01 00:00:00.0")) {
132                         return CertificateStatus.BEING_REVOKED;
133                 }
134                 return CertificateStatus.REVOKED;
135         }
136
137         public void issue() throws IOException {
138                 try {
139                         if (getStatus() != CertificateStatus.DRAFT) {
140                                 throw new IllegalStateException();
141                         }
142                         Notary.writeUserAgreement(ownerId, "CCA", "issue certificate", "", true, 0);
143
144                         PreparedStatement inserter = DatabaseConnection.getInstance().prepare(
145                                 "INSERT INTO emailcerts SET md=?, subject=?, coll_found=0, crt_name='', memid=?");
146                         inserter.setString(1, md);
147                         inserter.setString(2, dn);
148                         inserter.setInt(3, ownerId);
149                         inserter.execute();
150                         id = DatabaseConnection.lastInsertId(inserter);
151                         File csrFile = KeyStorage.locateCsr(id);
152                         csrName = csrFile.getPath();
153                         FileOutputStream fos = new FileOutputStream(csrFile);
154                         fos.write(csr.getBytes());
155                         fos.close();
156
157                         PreparedStatement updater = DatabaseConnection.getInstance().prepare(
158                                 "UPDATE emailcerts SET csr_name=? WHERE id=?");
159                         updater.setString(1, csrName);
160                         updater.setInt(2, id);
161                         updater.execute();
162                 } catch (SQLException e) {
163                         e.printStackTrace();
164                 }
165
166         }
167
168         public boolean waitFor(int max) throws SQLException, InterruptedException {
169                 long start = System.currentTimeMillis();
170                 while (getStatus().isUnstable()) {
171                         if (max != 0 && System.currentTimeMillis() - start > max) {
172                                 return false;
173                         }
174                         Thread.sleep((long) (2000 + Math.random() * 2000));
175                 }
176                 return true;
177         }
178
179         public void revoke() {
180                 try {
181                         if (getStatus() != CertificateStatus.ISSUED) {
182                                 throw new IllegalStateException();
183                         }
184                         PreparedStatement inserter = DatabaseConnection.getInstance().prepare(
185                                 "UPDATE emailcerts SET revoked = '1970-01-01' WHERE id=?");
186                         inserter.setInt(1, id);
187                         inserter.execute();
188                 } catch (SQLException e) {
189                         e.printStackTrace();
190                 }
191
192         }
193
194         public X509Certificate cert() throws IOException, GeneralSecurityException, SQLException {
195                 CertificateStatus status = getStatus();
196                 if (status != CertificateStatus.ISSUED) {
197                         throw new IllegalStateException(status + " is not wanted here.");
198                 }
199                 InputStream is = null;
200                 X509Certificate crt = null;
201                 try {
202                         is = new FileInputStream(crtName);
203                         CertificateFactory cf = CertificateFactory.getInstance("X.509");
204                         crt = (X509Certificate) cf.generateCertificate(is);
205                 } finally {
206                         if (is != null) {
207                                 is.close();
208                         }
209                 }
210                 return crt;
211         }
212
213         public Certificate renew() {
214                 return null;
215         }
216
217         public int getId() {
218                 return id;
219         }
220
221         public String getSerial() {
222                 return serial;
223         }
224
225         public String getDistinguishedName() {
226                 return dn;
227         }
228
229         public String getMessageDigest() {
230                 return md;
231         }
232
233         public int getOwnerId() {
234                 return ownerId;
235         }
236
237         public static Certificate getBySerial(String serial) {
238                 // TODO caching?
239                 try {
240                         return new Certificate(serial);
241                 } catch (IllegalArgumentException e) {
242
243                 }
244                 return null;
245         }
246
247 }