package org.cagrid.cds.service.impl.manager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cagrid.cds.service.exception.CDSInternalException; import org.cagrid.cds.service.exception.DelegationException; import org.cagrid.cds.service.impl.util.Errors; import org.cagrid.cds.service.impl.util.WrappedKey; import org.cagrid.gaards.pki.CertUtil; import org.cagrid.gaards.pki.KeyUtil; import org.cagrid.gaards.pki.SecurityUtil; import org.cagrid.tools.database.Database; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; public abstract class AbstractDBKeyManager implements KeyManager { private final static String PROVIDER = "BC"; private final static String TABLE = "key_manager"; private final static String ALIAS = "ALIAS"; private final static String PUBLIC_KEY = "PUBLIC_KEY"; private final static String PRIVATE_KEY = "PRIVATE_KEY"; private final static String IV = "IV"; private final static String CERTIFICATES_TABLE = "key_manager_certificates"; private final static String CERTIFICATE_NUMBER = "CERTIFICATE_NUMBER"; private final static String CERTIFICATE = "CERTIFICATE"; private boolean dbBuilt = false; private Database db; private Log log; public AbstractDBKeyManager(Database db) { this.log = LogFactory.getLog(this.getClass().getName()); this.db = db; SecurityUtil.init(); } public boolean exists(String alias) throws CDSInternalException { try { return db.exists(TABLE, ALIAS, alias); } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } } public abstract WrappedKey wrapPrivateKey(PrivateKey key) throws CDSInternalException; public abstract PrivateKey unwrapPrivateKey(WrappedKey wrappedKey) throws CDSInternalException; public KeyPair createAndStoreKeyPair(String alias, int keyLength) throws CDSInternalException { try { KeyPair pair = KeyUtil.generateRSAKeyPair(PROVIDER, keyLength); String publicKey = KeyUtil.writePublicKey(pair.getPublic()); WrappedKey privateKey = wrapPrivateKey(pair.getPrivate()); insertKeypair(alias, publicKey, privateKey); return pair; } catch (CDSInternalException f) { throw f; } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } } public X509Certificate[] getCertificates(String alias) throws CDSInternalException { this.buildDatabase(); if (exists(alias)) { List<X509Certificate> list = new ArrayList<X509Certificate>(); Connection c = null; try { c = db.getConnection(); PreparedStatement s = c.prepareStatement("select " + CERTIFICATE + " from " + CERTIFICATES_TABLE + " WHERE " + ALIAS + "= ? ORDER BY " + CERTIFICATE_NUMBER); s.setString(1, alias); ResultSet rs = s.executeQuery(); while (rs.next()) { list.add(CertUtil .loadCertificate(rs.getString(CERTIFICATE))); } rs.close(); s.close(); if (list.size() == 0) { return null; } else { X509Certificate[] certs = new X509Certificate[list.size()]; list.toArray(certs); return certs; } } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } finally { this.db.releaseConnection(c); } } else { return null; } } public PublicKey getPublicKey(String alias) throws CDSInternalException { this.buildDatabase(); PublicKey key = null; if (exists(alias)) { Connection c = null; try { c = this.db.getConnection(); PreparedStatement s = c.prepareStatement("select " + PUBLIC_KEY + " from " + TABLE + " WHERE " + ALIAS + "= ?"); s.setString(1, alias); ResultSet rs = s.executeQuery(); if (rs.next()) { key = KeyUtil.loadPublicKey(rs.getString(PUBLIC_KEY)); } rs.close(); s.close(); } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } finally { this.db.releaseConnection(c); } } return key; } public PrivateKey getPrivateKey(String alias) throws CDSInternalException { this.buildDatabase(); PrivateKey key = null; if (exists(alias)) { Connection c = null; try { c = this.db.getConnection(); PreparedStatement s = c.prepareStatement("select " + PRIVATE_KEY + "," + IV + " from " + TABLE + " WHERE " + ALIAS + "= ?"); s.setString(1, alias); ResultSet rs = s.executeQuery(); if (rs.next()) { byte[] keyData = rs.getBytes(PRIVATE_KEY); byte[] ivData = rs.getBytes(IV); key = unwrapPrivateKey(new WrappedKey(keyData, ivData)); } rs.close(); s.close(); } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } finally { this.db.releaseConnection(c); } } return key; } public void storeCertificates(String alias, X509Certificate[] cert) throws CDSInternalException, DelegationException { this.buildDatabase(); if (exists(alias)) { if ((cert != null) && (cert.length > 0)) { if (!cert[0].getPublicKey().equals(getPublicKey(alias))) { throw Errors.makeException(DelegationException.class, "The certificate provides is not bound to the public key generated."); } Connection c = null; try { c = this.db.getConnection(); for (int i = 0; i < cert.length; i++) { PreparedStatement s = c.prepareStatement("INSERT INTO " + CERTIFICATES_TABLE+ " SET " + ALIAS + "= ?," + CERTIFICATE_NUMBER + "= ?," + CERTIFICATE + "= ?"); s.setString(1, alias); s.setInt(2, (i+1)); s.setString(3, CertUtil.writeCertificate(cert[i])); s.execute(); s.close(); } } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } finally { this.db.releaseConnection(c); } } } else { throw Errors.makeException(CDSInternalException.class, "Cannot insert certificate, no key pair exists for the record (" + alias + ")."); } } private void insertKeypair(String alias, String publicKey, WrappedKey privateKey) throws CDSInternalException { this.buildDatabase(); if (!exists(alias)) { Connection c = null; try { c = this.db.getConnection(); PreparedStatement s = c.prepareStatement("INSERT INTO " + TABLE + " SET " + ALIAS + "= ?, " + PUBLIC_KEY + "= ?, " + PRIVATE_KEY + "= ?, " + IV + "= ?"); s.setString(1, alias); s.setString(2, publicKey); s.setBytes(3, privateKey.getWrappedKeyData()); s.setBytes(4, privateKey.getIV()); s.execute(); s.close(); } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } finally { this.db.releaseConnection(c); } } else { throw Errors.makeException(CDSInternalException.class, "Cannot insert key pair, a key pair already exists for the record (" + alias + ")."); } } public void delete(String alias) throws CDSInternalException { buildDatabase(); Connection c = null; try { c = this.db.getConnection(); PreparedStatement s = c.prepareStatement("DELETE FROM " + TABLE + " WHERE " + ALIAS + "= ?"); s.setString(1, alias); s.execute(); s.close(); s = c.prepareStatement("DELETE FROM " + CERTIFICATES_TABLE + " WHERE " + ALIAS + "= ?"); s.setString(1, alias); s.execute(); s.close(); } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } finally { this.db.releaseConnection(c); } } public void deleteAll() throws CDSInternalException { buildDatabase(); try { this.db.update("DELETE FROM " + TABLE); this.db.update("DELETE FROM " + CERTIFICATES_TABLE); dbBuilt = false; } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } } private void buildDatabase() throws CDSInternalException { if (!dbBuilt) { try { if (!this.db.tableExists(TABLE)) { String table = "CREATE TABLE " + TABLE + " (" + ALIAS + " VARCHAR(255) NOT NULL PRIMARY KEY," + PUBLIC_KEY + " TEXT NOT NULL," + PRIVATE_KEY + " BLOB NOT NULL," + IV + " BLOB, INDEX document_index (" + ALIAS + "));"; this.db.update(table); } if (!this.db.tableExists(CERTIFICATES_TABLE)) { String certTable = "CREATE TABLE " + CERTIFICATES_TABLE + " (" + ALIAS + " VARCHAR(255) NOT NULL," + CERTIFICATE_NUMBER + " INT," + CERTIFICATE + " TEXT, INDEX document_index (" + ALIAS + "));"; this.db.update(certTable); } dbBuilt = true; } catch (Exception e) { log.error(e.getMessage(), e); throw Errors.makeException(CDSInternalException.class, "Unexpected Database Error.", e); } } } protected Database getDB() { return db; } protected Log getLog() { return log; } }