Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.security.keystore.jce; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.KeyStoreSpi; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; import java.util.Date; import java.util.Enumeration; import java.util.List; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import mitm.common.hibernate.DatabaseAction; import mitm.common.hibernate.DatabaseActionExecutor; import mitm.common.hibernate.DatabaseActionExecutorBuilder; import mitm.common.hibernate.DatabaseException; import mitm.common.hibernate.DatabaseVoidAction; import mitm.common.hibernate.SessionManager; import mitm.common.security.keystore.KeyStoreRuntimeException; import mitm.common.security.keystore.dao.KeyStoreDAO; import mitm.common.security.keystore.dao.KeyStoreDAOHibernate; import mitm.common.security.keystore.hibernate.KeyStoreEntryHibernate; import mitm.common.security.keystore.hibernate.SerializableKeyEntry; import mitm.common.security.password.PBEncryption; import org.apache.commons.collections.iterators.IteratorEnumeration; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KeyStoreHibernate extends KeyStoreSpi { private static final Logger logger = LoggerFactory.getLogger(KeyStoreHibernate.class); /* * The number of times a database action should be retried when a ConstraintViolation occurs */ private final static int ACTION_RETRIES = 3; private final DatabaseActionExecutor databaseExecutor; private final PBEncryption encryptor; /** * Name of this store. Default when the name is not set. */ private String storeName = "default"; public KeyStoreHibernate(SessionManager sessionManager, PBEncryption encryptor) { this.databaseExecutor = DatabaseActionExecutorBuilder.createDatabaseActionExecutor(sessionManager); this.encryptor = encryptor; } @Override public Enumeration<String> engineAliases() throws KeyStoreRuntimeException { Enumeration<String> aliasesEnum = null; try { aliasesEnum = databaseExecutor.executeTransaction(new DatabaseAction<Enumeration<String>>() { @Override public Enumeration<String> doAction(Session session) { return getAliasesAction(session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } return aliasesEnum; } @SuppressWarnings("unchecked") private Enumeration<String> getAliasesAction(Session session) { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); List<String> aliases = dao.getAliases(); return new IteratorEnumeration(aliases.iterator()); } @Override public boolean engineContainsAlias(final String alias) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); return entry != null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } private KeyStoreEntryHibernate getEntryByAliasAction(String alias, Session session) { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); KeyStoreEntryHibernate entry = dao.getEntryByAlias(alias); return entry; } @Override public void engineDeleteEntry(final String alias) throws KeyStoreException { try { databaseExecutor.executeTransaction(new DatabaseVoidAction() { @Override public void doAction(Session session) { deleteEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); } catch (DatabaseException e) { logger.error("Database exception.", e); throw new KeyStoreException(e); } } private void deleteEntryByAliasAction(String alias, Session session) { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); KeyStoreEntryHibernate entry = dao.getEntryByAlias(alias); if (entry != null) { dao.delete(entry); } } @Override public Certificate engineGetCertificate(final String alias) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); return entry != null ? entry.getCertificate() : null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } @Override public String engineGetCertificateAlias(final Certificate certificate) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) throws DatabaseException { return getEntryByCertificateAction(certificate, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); return entry != null ? entry.getKeyAlias() : null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } private KeyStoreEntryHibernate getEntryByCertificateAction(Certificate certificate, Session session) throws DatabaseException { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); try { KeyStoreEntryHibernate entry = dao.getEntryByCertificate(certificate); return entry; } catch (CertificateEncodingException e) { throw new DatabaseException(e); } catch (NoSuchAlgorithmException e) { throw new DatabaseException(e); } catch (NoSuchProviderException e) { throw new DatabaseException(e); } } @Override public Certificate[] engineGetCertificateChain(final String alias) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); return entry != null ? entry.getChain() : null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } @Override public Date engineGetCreationDate(final String alias) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); return entry != null ? entry.getCreationDate() : null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } @Override public Key engineGetKey(final String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); if (entry == null) { return null; } byte[] serialized = entry.getEncodedKey(); SerializableKeyEntry serializableKeyEntry = SerializableKeyEntry.deserialize(serialized); Key key; if (password != null) { key = serializableKeyEntry.getKey(password, encryptor); } else { key = serializableKeyEntry.getKey(); } return key; } catch (DatabaseException e) { logger.error("Database exception.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (InvalidKeyException e) { logger.error("InvalidKeyException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (KeyStoreException e) { logger.error("KeyStoreException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (InvalidKeySpecException e) { logger.error("InvalidKeySpecException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (NoSuchProviderException e) { logger.error("NoSuchProviderException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (NoSuchPaddingException e) { logger.error("NoSuchPaddingException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (IllegalBlockSizeException e) { logger.error("IllegalBlockSizeException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (BadPaddingException e) { logger.error("BadPaddingException.", e); throw new UnrecoverableKeyException(e.getMessage()); } catch (IOException e) { logger.error("IOException.", e); throw new UnrecoverableKeyException(e.getMessage()); } } @Override public boolean engineIsCertificateEntry(final String alias) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); if (entry == null) { return false; } return entry.getEncodedKey() == null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } @Override public boolean engineIsKeyEntry(final String alias) throws KeyStoreRuntimeException { KeyStoreEntryHibernate entry; try { entry = databaseExecutor.executeTransaction(new DatabaseAction<KeyStoreEntryHibernate>() { @Override public KeyStoreEntryHibernate doAction(Session session) { return getEntryByAliasAction(alias, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); if (entry == null) { return false; } return entry.getEncodedKey() != null; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } @Override public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { /* not implemented */ } @Override public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter) throws IOException, NoSuchAlgorithmException, CertificateException { if (loadStoreParameter != null) { if (!(loadStoreParameter instanceof DatabaseKeyStoreLoadStoreParameter)) { throw new IOException("loadStoreParameter must be an DatabaseKeyStoreLoadStoreParameter."); } DatabaseKeyStoreLoadStoreParameter params = (DatabaseKeyStoreLoadStoreParameter) loadStoreParameter; this.storeName = params.getStoreName(); if (params.getSessionManager() != null) { this.databaseExecutor.setSessionManager(params.getSessionManager()); } } } @Override public void engineSetCertificateEntry(final String alias, final Certificate certificate) throws KeyStoreException { try { databaseExecutor.executeTransaction(new DatabaseVoidAction() { @Override public void doAction(Session session) { setCertificateEntryAction(alias, certificate, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); } catch (DatabaseException e) { logger.error("Database exception.", e); throw new KeyStoreException(e); } } private void setCertificateEntryAction(String alias, Certificate certificate, Session session) { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); KeyStoreEntryHibernate entry = dao.getEntryByAlias(alias); if (entry == null) { entry = new KeyStoreEntryHibernate(storeName, alias, new Date()); dao.makePersistent(entry); } entry.setEncodedKey(null); entry.setChain(null); entry.setCertificate(certificate); } @Override public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { throw new KeyStoreException("Not yet supported."); } @Override public void engineSetKeyEntry(final String alias, final Key key, final char[] password, final Certificate[] chain) throws KeyStoreException { try { databaseExecutor.executeTransaction(new DatabaseVoidAction() { @Override public void doAction(Session session) throws DatabaseException { setKeyEntryAction(alias, key, password, chain, session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); } catch (DatabaseException e) { logger.error("Database exception.", e); throw new KeyStoreException(e); } } private void setKeyEntryAction(String alias, Key key, char[] password, Certificate[] chain, Session session) throws DatabaseException { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); SerializableKeyEntry serializable; try { serializable = new SerializableKeyEntry(key, password, encryptor); Certificate certificate = null; if (chain != null && chain.length > 0) { certificate = chain[0]; } KeyStoreEntryHibernate entry = dao.getEntryByAlias(alias); if (entry == null) { entry = new KeyStoreEntryHibernate(storeName, alias, new Date()); dao.makePersistent(entry); } entry.setEncodedKey(serializable.serialize()); entry.setChain(chain); entry.setCertificate(certificate); } catch (InvalidKeyException e) { logger.error("InvalidKeyException.", e); throw new DatabaseException(e); } catch (InvalidKeySpecException e) { logger.error("InvalidKeySpecException.", e); throw new DatabaseException(e); } catch (NoSuchAlgorithmException e) { logger.error("NoSuchAlgorithmException.", e); throw new DatabaseException(e); } catch (NoSuchProviderException e) { logger.error("NoSuchProviderException.", e); throw new DatabaseException(e); } catch (NoSuchPaddingException e) { logger.error("NoSuchPaddingException.", e); throw new DatabaseException(e); } catch (IllegalBlockSizeException e) { logger.error("IllegalBlockSizeException.", e); throw new DatabaseException(e); } catch (BadPaddingException e) { logger.error("BadPaddingException.", e); throw new DatabaseException(e); } catch (IOException e) { logger.error("IOException.", e); throw new DatabaseException(e); } } @Override public int engineSize() throws KeyStoreRuntimeException { try { int size = databaseExecutor.executeTransaction(new DatabaseAction<Integer>() { @Override public Integer doAction(Session session) { return getSizeAction(session); } }, ACTION_RETRIES /* retry on a ConstraintViolationException */); return size; } catch (DatabaseException e) { logger.error("Database exception.", e); /* * engineAliases cannot throw a checked exception so * we will throw a runtime exception */ throw new KeyStoreRuntimeException(e); } } private int getSizeAction(Session session) { KeyStoreDAO dao = new KeyStoreDAOHibernate(storeName, session); return dao.getEntryCount().intValue(); } @Override public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { throw new IOException("Not yet supported."); } }