- private int id;
- private int ownerId;
- private int serial;
- private String dn;
- private String md;
- private String csrName;
- private String crtName;
- private String csr = null;
-
- public Certificate(int ownerId, String dn, String md, String csr) {
- this.ownerId = ownerId;
- this.dn = dn;
- this.md = md;
- this.csr = csr;
- }
-
- public Certificate(int serial) {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare(
- "SELECT id,subject, md, csr_name, crt_name,memid FROM `emailcerts` WHERE serial=?");
- ps.setInt(1, serial);
- ResultSet rs = ps.executeQuery();
- if (!rs.next()) {
- throw new IllegalArgumentException("Invalid mid " + serial);
- }
- this.id = rs.getInt(1);
- dn = rs.getString(2);
- md = rs.getString(3);
- csrName = rs.getString(4);
- crtName = rs.getString(5);
- ownerId = rs.getInt(6);
- this.serial = serial;
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- public enum CertificateStatus {
- /**
- * This certificate is not in the database, has no id and only exists as
- * this java object.
- */
- DRAFT(false),
- /**
- * The certificate has been written to the database and is waiting for
- * the signer to sign it.
- */
- SIGNING(true),
- /**
- * The certificate has been signed. It is stored in the database.
- * {@link Certificate#cert()} is valid.
- */
- ISSUED(false),
- /**
- * The cetrificate is about to be revoked by the signer bot.
- */
- BEING_REVOKED(true),
-
- /**
- * The certificate has been revoked.
- */
- REVOKED(false),
-
- /**
- * If this certificate cannot be updated because an error happened in
- * the signer.
- */
- ERROR(false);
-
- private boolean unstable;
-
- private CertificateStatus(boolean unstable) {
- this.unstable = unstable;
- }
-
- /**
- * Checks, iff this certificate stage will be left by signer actions.
- *
- * @return True, iff this certificate stage will be left by signer
- * actions.
- */
- public boolean isUnstable() {
- return unstable;
- }
-
- }
-
- public CertificateStatus getStatus() throws SQLException {
- if (id == 0) {
- return CertificateStatus.DRAFT;
- }
- PreparedStatement searcher = DatabaseConnection.getInstance().prepare(
- "SELECT crt_name, created, revoked, warning FROM emailcerts WHERE id=?");
- searcher.setInt(1, id);
- ResultSet rs = searcher.executeQuery();
- if (!rs.next()) {
- throw new IllegalStateException("Certificate not in Database");
- }
- if (rs.getInt(4) >= 3) {
- return CertificateStatus.ERROR;
- }
-
- if (rs.getString(2) == null) {
- return CertificateStatus.SIGNING;
- }
- crtName = rs.getString(1);
- if (rs.getTime(2) != null && rs.getTime(3) == null) {
- return CertificateStatus.ISSUED;
- }
- if (rs.getTime(2) != null && rs.getString(3).equals("1970-01-01 00:00:00.0")) {
- return CertificateStatus.BEING_REVOKED;
- }
- return CertificateStatus.REVOKED;
- }
-
- public void issue() throws IOException {
- try {
- if (getStatus() != CertificateStatus.DRAFT) {
- throw new IllegalStateException();
- }
- PreparedStatement inserter = DatabaseConnection.getInstance().prepare(
- "INSERT INTO emailcerts SET md=?, subject=?, coll_found=0, crt_name='', memid=?");
- inserter.setString(1, md);
- inserter.setString(2, dn);
- inserter.setInt(3, ownerId);
- inserter.execute();
- id = DatabaseConnection.lastInsertId(inserter);
- File csrFile = KeyStorage.locateCsr(id);
- csrName = csrFile.getPath();
- FileOutputStream fos = new FileOutputStream(csrFile);
- fos.write(csr.getBytes());
- fos.close();
-
- PreparedStatement updater = DatabaseConnection.getInstance().prepare(
- "UPDATE emailcerts SET csr_name=? WHERE id=?");
- updater.setString(1, csrName);
- updater.setInt(2, id);
- updater.execute();
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- }
-
- public boolean waitFor(int max) throws SQLException, InterruptedException {
- long start = System.currentTimeMillis();
- while (getStatus().isUnstable()) {
- if (max != 0 && System.currentTimeMillis() - start > max) {
- return false;
- }
- Thread.sleep((long) (2000 + Math.random() * 2000));
- }
- return true;
- }
-
- public void revoke() {
- try {
- if (getStatus() != CertificateStatus.ISSUED) {
- throw new IllegalStateException();
- }
- PreparedStatement inserter = DatabaseConnection.getInstance().prepare(
- "UPDATE emailcerts SET revoked = '1970-01-01' WHERE id=?");
- inserter.setInt(1, id);
- inserter.execute();
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- }
-
- public X509Certificate cert() throws IOException, GeneralSecurityException, SQLException {
- CertificateStatus status = getStatus();
- if (status != CertificateStatus.ISSUED) {
- throw new IllegalStateException(status + " is not wanted here.");
- }
- InputStream is = null;
- X509Certificate crt = null;
- try {
- is = new FileInputStream(crtName);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- crt = (X509Certificate) cf.generateCertificate(is);
- } finally {
- if (is != null) {
- is.close();
- }
- }
- return crt;
- }
-
- public Certificate renew() {
- return null;
- }
-
- public int getId() {
- return id;
- }
-
- public int getSerial() {
- return serial;
- }
-
- public String getDistinguishedName() {
- return dn;
- }
-
- public String getMessageDigest() {
- return md;
- }
-
- public int getOwnerId() {
- return ownerId;
- }
+
+ public enum SANType {
+ EMAIL("email"), DNS("DNS");
+
+ private final String opensslName;
+
+ private SANType(String opensslName) {
+ this.opensslName = opensslName;
+ }
+
+ public String getOpensslName() {
+ return opensslName;
+ }
+ }
+
+ public static class SubjectAlternateName {
+
+ private SANType type;
+
+ private String name;
+
+ public SubjectAlternateName(SANType type, String name) {
+ this.type = type;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public SANType getType() {
+ return type;
+ }
+
+ }
+
+ public enum CSRType {
+ CSR, SPKAC;
+ }
+
+ private int id;
+
+ private int ownerId;
+
+ private String serial;
+
+ private String dn;
+
+ private String md;
+
+ private String csrName;
+
+ private String crtName;
+
+ private String csr = null;
+
+ private CSRType csrType;
+
+ private List<SubjectAlternateName> sans;
+
+ public Certificate(int ownerId, String dn, String md, String csr, CSRType csrType, SubjectAlternateName... sans) {
+ this.ownerId = ownerId;
+ this.dn = dn;
+ this.md = md;
+ this.csr = csr;
+ this.csrType = csrType;
+ this.sans = Arrays.asList(sans);
+ }
+
+ private Certificate(String serial) {
+ try {
+ PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id,subject, md, csr_name, crt_name,memid FROM `emailcerts` WHERE serial=?");
+ ps.setString(1, serial);
+ ResultSet rs = ps.executeQuery();
+ if ( !rs.next()) {
+ throw new IllegalArgumentException("Invalid mid " + serial);
+ }
+ this.id = rs.getInt(1);
+ dn = rs.getString(2);
+ md = rs.getString(3);
+ csrName = rs.getString(4);
+ crtName = rs.getString(5);
+ ownerId = rs.getInt(6);
+ this.serial = serial;
+
+ PreparedStatement ps2 = DatabaseConnection.getInstance().prepare("SELECT contents, type FROM `subjectAlternativeNames` WHERE certId=?");
+ ps2.setInt(1, id);
+ ResultSet rs2 = ps2.executeQuery();
+ sans = new LinkedList<>();
+ while (rs2.next()) {
+ sans.add(new SubjectAlternateName(SANType.valueOf(rs2.getString("type").toUpperCase()), rs2.getString("contents")));
+ }
+ rs2.close();
+
+ rs.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public enum CertificateStatus {
+ /**
+ * This certificate is not in the database, has no id and only exists as
+ * this java object.
+ */
+ DRAFT(),
+ /**
+ * The certificate has been signed. It is stored in the database.
+ * {@link Certificate#cert()} is valid.
+ */
+ ISSUED(),
+
+ /**
+ * The certificate has been revoked.
+ */
+ REVOKED(),
+
+ /**
+ * If this certificate cannot be updated because an error happened in
+ * the signer.
+ */
+ ERROR();
+
+ private CertificateStatus() {}
+
+ }
+
+ public CertificateStatus getStatus() throws SQLException {
+ if (id == 0) {
+ return CertificateStatus.DRAFT;
+ }
+ PreparedStatement searcher = DatabaseConnection.getInstance().prepare("SELECT crt_name, created, revoked, serial FROM emailcerts WHERE id=?");
+ searcher.setInt(1, id);
+ ResultSet rs = searcher.executeQuery();
+ if ( !rs.next()) {
+ throw new IllegalStateException("Certificate not in Database");
+ }
+
+ crtName = rs.getString(1);
+ serial = rs.getString(4);
+ if (rs.getTime(2) == null) {
+ return CertificateStatus.DRAFT;
+ }
+ if (rs.getTime(2) != null && rs.getTime(3) == null) {
+ return CertificateStatus.ISSUED;
+ }
+ return CertificateStatus.REVOKED;
+ }
+
+ public Job issue() throws IOException, SQLException {
+ if (getStatus() != CertificateStatus.DRAFT) {
+ throw new IllegalStateException();
+ }
+ Notary.writeUserAgreement(ownerId, "CCA", "issue certificate", "", true, 0);
+
+ PreparedStatement inserter = DatabaseConnection.getInstance().prepare("INSERT INTO emailcerts SET md=?, subject=?, csr_type=?, crt_name='', memid=?, profile=1");
+ inserter.setString(1, md);
+ inserter.setString(2, dn);
+ inserter.setString(3, csrType.toString());
+ inserter.setInt(4, ownerId);
+ inserter.execute();
+ id = DatabaseConnection.lastInsertId(inserter);
+ File csrFile = KeyStorage.locateCsr(id);
+ csrName = csrFile.getPath();
+ FileOutputStream fos = new FileOutputStream(csrFile);
+ fos.write(csr.getBytes());
+ fos.close();
+
+ // TODO draft to insert SANs
+ PreparedStatement san = DatabaseConnection.getInstance().prepare("INSERT INTO subjectAlternativeNames SET certId=?, contents=?, type=?");
+ for (SubjectAlternateName subjectAlternateName : sans) {
+ san.setInt(1, id);
+ san.setString(2, subjectAlternateName.getName());
+ san.setString(3, subjectAlternateName.getType().getOpensslName());
+ san.execute();
+ }
+
+ PreparedStatement updater = DatabaseConnection.getInstance().prepare("UPDATE emailcerts SET csr_name=? WHERE id=?");
+ updater.setString(1, csrName);
+ updater.setInt(2, id);
+ updater.execute();
+ return Job.submit(this, JobType.SIGN);
+
+ }
+
+ public Job revoke() throws SQLException {
+ if (getStatus() != CertificateStatus.ISSUED) {
+ throw new IllegalStateException();
+ }
+ return Job.submit(this, JobType.REVOKE);
+
+ }
+
+ public X509Certificate cert() throws IOException, GeneralSecurityException, SQLException {
+ CertificateStatus status = getStatus();
+ if (status != CertificateStatus.ISSUED) {
+ throw new IllegalStateException(status + " is not wanted here.");
+ }
+ InputStream is = null;
+ X509Certificate crt = null;
+ try {
+ is = new FileInputStream(crtName);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ crt = (X509Certificate) cf.generateCertificate(is);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ return crt;
+ }
+
+ public Certificate renew() {
+ return null;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getSerial() {
+ try {
+ getStatus();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } // poll changes
+ return serial;
+ }
+
+ public String getDistinguishedName() {
+ return dn;
+ }
+
+ public String getMessageDigest() {
+ return md;
+ }
+
+ public int getOwnerId() {
+ return ownerId;
+ }
+
+ public List<SubjectAlternateName> getSans() {
+ return Collections.unmodifiableList(sans);
+ }
+
+ public static Certificate getBySerial(String serial) {
+ // TODO caching?
+ try {
+ return new Certificate(serial);
+ } catch (IllegalArgumentException e) {
+
+ }
+ return null;
+ }