X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2FCertificate.java;h=a2645c62d22ebe231b496e15fe22519602febc7d;hb=b0a970a60d0001260594468f3ffffbf92a19bc44;hp=11747bd5707880914149ccd449c57cae8f0fdbf8;hpb=3fcea04f8aeecdf2b8d4ef36498c65a4d271884b;p=gigi.git diff --git a/src/org/cacert/gigi/Certificate.java b/src/org/cacert/gigi/Certificate.java index 11747bd5..a2645c62 100644 --- a/src/org/cacert/gigi/Certificate.java +++ b/src/org/cacert/gigi/Certificate.java @@ -1,6 +1,8 @@ package org.cacert.gigi; +import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; @@ -9,118 +11,271 @@ import java.security.cert.X509Certificate; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + import org.cacert.gigi.database.DatabaseConnection; +import org.cacert.gigi.util.Job; +import org.cacert.gigi.util.Job.JobType; +import org.cacert.gigi.util.KeyStorage; +import org.cacert.gigi.util.Notary; public class Certificate { - int id; - int serial; - String dn; - String md; - String csrName; - String crtName; - - // created, modified, revoked, expire - public enum CertificateStatus { - DRAFT(false), BEEING_ISSUED(true), ISSUED(false), BEEING_REVOKED(true), REVOKED( - false); - - boolean unstable; - - private CertificateStatus(boolean unstable) { - this.unstable = unstable; - } - 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 FROM emailcerts WHERE id=?"); - searcher.setInt(1, id); - ResultSet rs = searcher.executeQuery(); - if (!rs.next()) { - throw new IllegalStateException("Certificate not in Database"); - } - if (rs.getString(2) == null) { - return CertificateStatus.BEEING_ISSUED; - } - 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.BEEING_REVOKED; - } - return CertificateStatus.REVOKED; - } - - public void issue() { - try { - if (getStatus() != CertificateStatus.DRAFT) { - throw new IllegalStateException(); - } - PreparedStatement inserter = DatabaseConnection - .getInstance() - .prepare( - "INSERT INTO emailcerts SET csr_name =?, md=?, subject=?, coll_found=0, crt_name=''"); - inserter.setString(1, csrName); - inserter.setString(2, md); - inserter.setString(3, dn); - inserter.execute(); - id = DatabaseConnection.lastInsertId(inserter); - } 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 { - 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 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 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 `certs` 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 certs 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 certs 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 certs 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 getSans() { + return Collections.unmodifiableList(sans); + } + + public static Certificate getBySerial(String serial) { + // TODO caching? + try { + return new Certificate(serial); + } catch (IllegalArgumentException e) { + + } + return null; + } }