/* * Commons eID Project. * Copyright (C) 2008-2013 FedICT. * Copyright (C) 2014-2016 e-Contract.be BVBA. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ package be.fedict.commons.eid.jca; import java.awt.Component; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.Key; import java.security.KeyStore; import java.security.KeyStore.Entry; import java.security.KeyStore.LoadStoreParameter; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.ProtectionParameter; import java.security.KeyStore.TrustedCertificateEntry; import java.security.KeyStoreException; import java.security.KeyStoreSpi; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableEntryException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Vector; import javax.smartcardio.CardTerminal; import javax.swing.JFrame; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import be.fedict.commons.eid.client.BeIDCard; import be.fedict.commons.eid.client.BeIDCards; import be.fedict.commons.eid.client.CancelledException; import be.fedict.commons.eid.client.FileType; import be.fedict.commons.eid.client.impl.VoidLogger; import be.fedict.commons.eid.client.spi.BeIDCardUI; import be.fedict.commons.eid.client.spi.BeIDCardsUI; import be.fedict.commons.eid.client.spi.Logger; import be.fedict.commons.eid.dialogs.DefaultBeIDCardUI; import be.fedict.commons.eid.dialogs.DefaultBeIDCardsUI; import be.fedict.commons.eid.dialogs.Messages; /** * eID based JCA {@link KeyStore}. Used to load eID key material via standard * JCA API calls. Once the JCA security provider has been registered you have a * new key store available named "BeID". Two key aliases are available: * <ul> * <li>"Authentication" which gives you access to the eID authentication private * key and corresponding certificate chain.</li> * <li>"Signature" which gives you access to the eID non-repudiation private key * and corresponding certificate chain.</li> * </ul> * Further the Citizen CA certificate can be accessed via the "CA" alias, the * Root CA certificate can be accessed via the "Root" alias, and the national * registration certificate can be accessed via the "RRN" alias. * <p/> * Supports the eID specific {@link BeIDKeyStoreParameter} key store parameter. * You can also let any {@link JFrame} implement the * {@link KeyStore.LoadStoreParameter} interface. If you pass this to * {@link KeyStore#load(LoadStoreParameter)} the keystore will use that Swing * frame as parent for positioning the dialogs. * <p/> * Usage: * <p/> * * <pre> * import java.security.KeyStore; * import java.security.cert.X509Certificate; * import java.security.PrivateKey; * * ... * KeyStore keyStore = KeyStore.getInstance("BeID"); * keyStore.load(null); * X509Certificate authnCertificate = (X509Certificate) keyStore * .getCertificate("Authentication"); * PrivateKey authnPrivateKey = (PrivateKey) keyStore.getKey( * "Authentication", null); * Certificate[] signCertificateChain = keyStore.getCertificateChain("Signature"); * </pre> * * @author Frank Cornelis * @see BeIDKeyStoreParameter * @see BeIDProvider */ public class BeIDKeyStore extends KeyStoreSpi { private static final Log LOG = LogFactory.getLog(BeIDKeyStore.class); private BeIDKeyStoreParameter keyStoreParameter; private BeIDCard beIDCard; private List<X509Certificate> authnCertificateChain; private List<X509Certificate> signCertificateChain; private List<X509Certificate> rrnCertificateChain; private X509Certificate citizenCaCertificate; private X509Certificate rootCaCertificate; private X509Certificate authnCertificate; private X509Certificate signCertificate; private X509Certificate rrnCertificate; private CardTerminal cardTerminal; @Override public Key engineGetKey(final String alias, final char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { LOG.debug("engineGetKey: " + alias); final BeIDCard beIDCard = getBeIDCard(); boolean logoff; boolean allowFailingLogoff; boolean autoRecovery; String applicationName; if (null == this.keyStoreParameter) { logoff = false; allowFailingLogoff = false; autoRecovery = false; applicationName = null; } else { logoff = this.keyStoreParameter.getLogoff(); allowFailingLogoff = this.keyStoreParameter.isAllowFailingLogoff(); autoRecovery = this.keyStoreParameter.getAutoRecovery(); applicationName = this.keyStoreParameter.getApplicationName(); } if ("Authentication".equals(alias)) { final BeIDPrivateKey beIDPrivateKey = new BeIDPrivateKey( FileType.AuthentificationCertificate, beIDCard, logoff, allowFailingLogoff, autoRecovery, this, applicationName); return beIDPrivateKey; } if ("Signature".equals(alias)) { final BeIDPrivateKey beIDPrivateKey = new BeIDPrivateKey( FileType.NonRepudiationCertificate, beIDCard, logoff, allowFailingLogoff, autoRecovery, this, applicationName); return beIDPrivateKey; } return null; } @Override public Certificate[] engineGetCertificateChain(final String alias) { LOG.debug("engineGetCertificateChain: " + alias); final BeIDCard beIDCard = getBeIDCard(); if ("Signature".equals(alias)) { try { if (null == this.signCertificateChain) { this.signCertificateChain = beIDCard .getSigningCertificateChain(); this.signCertificate = this.signCertificateChain.get(0); this.citizenCaCertificate = this.signCertificateChain .get(1); this.rootCaCertificate = this.signCertificateChain.get(2); } } catch (final Exception ex) { LOG.error("error: " + ex.getMessage(), ex); return null; } return this.signCertificateChain.toArray(new X509Certificate[]{}); } if ("Authentication".equals(alias)) { try { if (null == this.authnCertificateChain) { this.authnCertificateChain = beIDCard .getAuthenticationCertificateChain(); this.authnCertificate = this.authnCertificateChain.get(0); this.citizenCaCertificate = this.authnCertificateChain .get(1); this.rootCaCertificate = this.authnCertificateChain.get(2); } } catch (final Exception ex) { LOG.error("error: " + ex.getMessage(), ex); return null; } return this.authnCertificateChain.toArray(new X509Certificate[]{}); } if ("RRN".equals(alias)) { if (null == this.rrnCertificateChain) { try { this.rrnCertificateChain = beIDCard .getRRNCertificateChain(); } catch (Exception e) { LOG.error("error: " + e.getMessage(), e); return null; } this.rrnCertificate = this.rrnCertificateChain.get(0); this.rootCaCertificate = this.rrnCertificateChain.get(1); } return this.rrnCertificateChain.toArray(new X509Certificate[]{}); } return null; } @Override public Certificate engineGetCertificate(final String alias) { LOG.debug("engineGetCertificate: " + alias); final BeIDCard beIDCard = getBeIDCard(); if ("Signature".equals(alias)) { try { if (null == this.signCertificate) { this.signCertificate = beIDCard.getSigningCertificate(); } } catch (final Exception ex) { LOG.warn("error: " + ex.getMessage(), ex); return null; } return this.signCertificate; } if ("Authentication".equals(alias)) { try { if (null == this.authnCertificate) { this.authnCertificate = beIDCard .getAuthenticationCertificate(); } } catch (final Exception ex) { LOG.warn("error: " + ex.getMessage(), ex); return null; } return this.authnCertificate; } if ("CA".equals(alias)) { try { if (null == this.citizenCaCertificate) { this.citizenCaCertificate = beIDCard.getCACertificate(); } } catch (Exception e) { LOG.warn("error: " + e.getMessage(), e); return null; } return this.citizenCaCertificate; } if ("Root".equals(alias)) { try { if (null == this.rootCaCertificate) { this.rootCaCertificate = beIDCard.getRootCACertificate(); } } catch (Exception e) { LOG.warn("error: " + e.getMessage(), e); return null; } return this.rootCaCertificate; } if ("RRN".equals(alias)) { try { if (null == this.rrnCertificate) { this.rrnCertificate = beIDCard.getRRNCertificate(); } } catch (Exception e) { LOG.warn("error: " + e.getMessage(), e); return null; } return this.rrnCertificate; } return null; } @Override public Date engineGetCreationDate(final String alias) { final X509Certificate certificate = (X509Certificate) this .engineGetCertificate(alias); if (null == certificate) { return null; } return certificate.getNotBefore(); } @Override public void engineSetKeyEntry(final String alias, final Key key, final char[] password, final Certificate[] chain) throws KeyStoreException { throw new KeyStoreException(); } @Override public void engineSetKeyEntry(final String alias, final byte[] key, final Certificate[] chain) throws KeyStoreException { throw new KeyStoreException(); } @Override public void engineSetCertificateEntry(final String alias, final Certificate cert) throws KeyStoreException { throw new KeyStoreException(); } @Override public void engineDeleteEntry(final String alias) throws KeyStoreException { throw new KeyStoreException(); } @Override public Enumeration<String> engineAliases() { LOG.debug("engineAliases"); final Vector<String> aliases = new Vector<String>(); aliases.add("Authentication"); aliases.add("Signature"); aliases.add("CA"); aliases.add("Root"); aliases.add("RRN"); return aliases.elements(); } @Override public boolean engineContainsAlias(final String alias) { LOG.debug("engineContainsAlias: " + alias); if ("Authentication".equals(alias)) { return true; } if ("Signature".equals(alias)) { return true; } if ("Root".equals(alias)) { return true; } if ("CA".equals(alias)) { return true; } if ("RRN".equals(alias)) { return true; } return false; } @Override public int engineSize() { return 2; } @Override public boolean engineIsKeyEntry(final String alias) { LOG.debug("engineIsKeyEntry: " + alias); if ("Authentication".equals(alias)) { return true; } if ("Signature".equals(alias)) { return true; } return false; } @Override public boolean engineIsCertificateEntry(final String alias) { LOG.debug("engineIsCertificateEntry: " + alias); if ("Root".equals(alias)) { return true; } if ("CA".equals(alias)) { return true; } if ("RRN".equals(alias)) { return true; } return false; } @Override public void engineStore(LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException { LOG.debug("engineStore"); super.engineStore(param); } @Override public Entry engineGetEntry(String alias, ProtectionParameter protParam) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { LOG.debug("engineGetEntry: " + alias); if ("Authentication".equals(alias) || "Signature".equals(alias)) { PrivateKey privateKey = (PrivateKey) engineGetKey(alias, null); Certificate[] chain = engineGetCertificateChain(alias); PrivateKeyEntry privateKeyEntry = new PrivateKeyEntry(privateKey, chain); return privateKeyEntry; } if ("CA".equals(alias) || "Root".equals(alias) || "RRN".equals(alias)) { Certificate certificate = engineGetCertificate(alias); TrustedCertificateEntry trustedCertificateEntry = new TrustedCertificateEntry( certificate); return trustedCertificateEntry; } return super.engineGetEntry(alias, protParam); } @Override public void engineSetEntry(String alias, Entry entry, ProtectionParameter protParam) throws KeyStoreException { LOG.debug("engineSetEntry: " + alias); super.engineSetEntry(alias, entry, protParam); } @Override public boolean engineEntryInstanceOf(String alias, Class<? extends Entry> entryClass) { LOG.debug("engineEntryInstanceOf: " + alias); return super.engineEntryInstanceOf(alias, entryClass); } @Override public String engineGetCertificateAlias(final Certificate cert) { return null; } @Override public void engineStore(final OutputStream stream, final char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { } @Override public void engineLoad(final InputStream stream, final char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { } @Override public void engineLoad(final LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException { LOG.debug("engineLoad"); /* * Allows for a KeyStore to be re-loaded several * times. */ this.beIDCard = null; this.authnCertificateChain = null; this.signCertificateChain = null; this.rrnCertificateChain = null; this.authnCertificate = null; this.signCertificate = null; this.citizenCaCertificate = null; this.rootCaCertificate = null; this.rrnCertificate = null; if (null == param) { return; } if (param instanceof BeIDKeyStoreParameter) { this.keyStoreParameter = (BeIDKeyStoreParameter) param; return; } if (param instanceof JFrame) { this.keyStoreParameter = new BeIDKeyStoreParameter(); JFrame frame = (JFrame) param; this.keyStoreParameter.setParentComponent(frame); return; } throw new NoSuchAlgorithmException(); } private BeIDCard getBeIDCard() { return getBeIDCard(false); } public BeIDCard getBeIDCard(boolean recover) { boolean cardReaderStickiness; if (null != this.keyStoreParameter) { cardReaderStickiness = this.keyStoreParameter .getCardReaderStickiness(); } else { cardReaderStickiness = false; } if (recover) { LOG.debug("recovering from error"); this.beIDCard = null; } if (null != this.beIDCard) { return this.beIDCard; } if (null != this.keyStoreParameter) { this.beIDCard = this.keyStoreParameter.getBeIDCard(); } if (null != this.beIDCard) { return this.beIDCard; } Component parentComponent; Locale locale; Logger logger; if (null != this.keyStoreParameter) { parentComponent = this.keyStoreParameter.getParentComponent(); locale = this.keyStoreParameter.getLocale(); logger = this.keyStoreParameter.getLogger(); } else { parentComponent = null; locale = null; logger = null; } if (null == locale) { locale = Locale.getDefault(); } if (null == logger) { logger = new VoidLogger(); } final Messages messages = Messages.getInstance(locale); final BeIDCardsUI ui = new DefaultBeIDCardsUI(parentComponent, messages); final BeIDCards beIDCards = new BeIDCards(logger, ui); beIDCards.setLocale(locale); try { CardTerminal stickyCardTerminal; if (cardReaderStickiness) { stickyCardTerminal = this.cardTerminal; } else { stickyCardTerminal = null; } this.beIDCard = beIDCards.getOneBeIDCard(stickyCardTerminal); if (cardReaderStickiness) { this.cardTerminal = this.beIDCard.getCardTerminal(); LOG.debug("sticky card reader: " + this.cardTerminal.getName()); } final BeIDCardUI userInterface = new DefaultBeIDCardUI( parentComponent, messages); this.beIDCard.setUI(userInterface); } catch (final CancelledException cex) { throw new SecurityException("user cancelled"); } if (null == this.beIDCard) { throw new SecurityException("missing eID card"); } return this.beIDCard; } }