X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2FCertificate.java;h=6b06926f65767829015ae520acc7af12fd7e1963;hb=d04f2d292d57bd50cef0d229ef5ab60cdc8492a9;hp=378b3a7001fc10e39fc01589eee794ce5ca2356a;hpb=59d0c083b6d155d227fbc3ce896922a15cc7846d;p=gigi.git diff --git a/src/org/cacert/gigi/Certificate.java b/src/org/cacert/gigi/Certificate.java index 378b3a70..6b06926f 100644 --- a/src/org/cacert/gigi/Certificate.java +++ b/src/org/cacert/gigi/Certificate.java @@ -11,223 +11,280 @@ 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 { - 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 sans; + + private CertificateProfile profile; + + public Certificate(int ownerId, String dn, String md, String csr, CSRType csrType, CertificateProfile profile, SubjectAlternateName... sans) { + this.ownerId = ownerId; + this.dn = dn; + this.md = md; + this.csr = csr; + this.csrType = csrType; + this.profile = profile; + this.sans = Arrays.asList(sans); + } + + private Certificate(String serial) { + try { + PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id,subject, md, csr_name, crt_name,memid, profile 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); + profile = CertificateProfile.getById(rs.getInt(7)); + 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=?"); + inserter.setString(1, md); + inserter.setString(2, dn); + inserter.setString(3, csrType.toString()); + inserter.setInt(4, ownerId); + inserter.setInt(5, profile.getId()); + 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 CertificateProfile getProfile() { + return profile; + } + + public static Certificate getBySerial(String serial) { + // TODO caching? + try { + return new Certificate(serial); + } catch (IllegalArgumentException e) { + + } + return null; + } }