/* * DSS - Digital Signature Services * * Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com * * This file is part of the "DSS - Digital Signature Services" project. * * "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the * License, or (at your option) any later version. * * DSS 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 * "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>. */ package eu.europa.ec.markt.dss.signature.token; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.ProtectionParameter; import java.security.KeyStoreException; import java.security.Provider; import java.security.ProviderException; import java.security.Security; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Random; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import eu.europa.ec.markt.dss.exception.DSSBadPasswordException; import eu.europa.ec.markt.dss.exception.DSSBadPasswordException.MSG; import eu.europa.ec.markt.dss.exception.DSSConfigurationException; import eu.europa.ec.markt.dss.exception.DSSException; /** * PKCS11 token with callback * * @version $Revision: 968 $ - $Date: 2011-06-14 16:34:14 +0200 (mar., 14 juin 2011) $ */ public class Pkcs11SignatureToken extends AbstractSignatureTokenConnection { private Provider _pkcs11Provider; private String pkcs11Path; private KeyStore _keyStore; final private PasswordInputCallback callback; private int slotIndex; private static int smartCardNameIndex = 0; /** * Create the SignatureTokenConnection, using the provided path for the library. * * @param pkcs11Path */ public Pkcs11SignatureToken(String pkcs11Path) { this(pkcs11Path, (PasswordInputCallback) null); this.slotIndex = 0; } /** * Create the SignatureTokenConnection, using the provided path for the library and a way of retrieving the password * from the user. The default constructor for CallbackPkcs11SignatureToken. * * @param pkcs11Path * @param callback */ public Pkcs11SignatureToken(String pkcs11Path, PasswordInputCallback callback) { this.pkcs11Path = pkcs11Path; this.callback = callback; this.slotIndex = 0; } /** * Sometimes, the password is known in advance. This create a SignatureTokenConnection and the keys will be accessed * using the provided password. The default constructor for CallbackPkcs11SignatureToken. * * @param pkcs11Path * @param password */ public Pkcs11SignatureToken(String pkcs11Path, char[] password) { this(pkcs11Path, new PrefilledPasswordCallback(password)); this.slotIndex = 0; } /** * Sometimes, multiple SmartCard reader is connected. To create a connection on a specific one, slotIndex is used. * This create a SignatureTokenConnection and the keys will be accessed using the provided password. * * @param pkcs11Path * @param callback * @param slotIndex */ public Pkcs11SignatureToken(String pkcs11Path, PasswordInputCallback callback, int slotIndex) { this(pkcs11Path, callback); this.slotIndex = slotIndex; } /** * Sometimes, multiple SmartCard reader is connected. To create a connection on a specific one, slotIndex is used. * This Create the SignatureTokenConnection, using the provided path for the library and a way of retrieving the * password from the user. * * @param pkcs11Path * @param password * @param slotIndex */ public Pkcs11SignatureToken(String pkcs11Path, char[] password, int slotIndex) { this(pkcs11Path, password); this.slotIndex = slotIndex; } @SuppressWarnings("restriction") private Provider getProvider() { try { if (_pkcs11Provider == null) { // check if the provider already exists final Provider[] providers = Security.getProviders(); if (providers != null) { for (final Provider provider : providers) { final String providerInfo = provider.getInfo(); if (providerInfo.contains(getPkcs11Path())) { _pkcs11Provider = provider; return provider; } } } // provider not already installed installProvider(); } return _pkcs11Provider; } catch (ProviderException ex) { throw new DSSConfigurationException(DSSConfigurationException.MSG.NOT_PKCS11_LIB, ex); } } private void installProvider() { /* The smartCardNameIndex int is added at the end of the smartCard name in order to enable the successive loading of multiple pkcs11 libraries */ String aPKCS11LibraryFileName = getPkcs11Path(); String pkcs11ConfigSettings = "name = SmartCard" + smartCardNameIndex + "\n" + "library = " + aPKCS11LibraryFileName + "\nslotListIndex = " + slotIndex; byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes(); ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes); sun.security.pkcs11.SunPKCS11 pkcs11 = new sun.security.pkcs11.SunPKCS11(confStream); _pkcs11Provider = pkcs11; Security.addProvider(_pkcs11Provider); smartCardNameIndex++; } @SuppressWarnings("restriction") private KeyStore getKeyStore() throws KeyStoreException { if (_keyStore == null) { _keyStore = KeyStore.getInstance("PKCS11", getProvider()); try { _keyStore.load(new KeyStore.LoadStoreParameter() { @Override public ProtectionParameter getProtectionParameter() { return new KeyStore.CallbackHandlerProtection(new CallbackHandler() { @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback c : callbacks) { if (c instanceof PasswordCallback) { ((PasswordCallback) c).setPassword(callback.getPassword()); return; } } throw new RuntimeException("No password callback"); } }); } }); } catch (Exception e) { if (e instanceof sun.security.pkcs11.wrapper.PKCS11Exception) { if ("CKR_PIN_INCORRECT".equals(e.getMessage())) { throw new DSSBadPasswordException(MSG.PKCS11_BAD_PASSWORD, e); } } throw new KeyStoreException("Can't initialize Sun PKCS#11 security provider. Reason: " + getCauseMessage(e), e); } } return _keyStore; } private String getPkcs11Path() { return pkcs11Path; } @Override public void close() { if (_pkcs11Provider != null) { try { Security.removeProvider(_pkcs11Provider.getName()); } catch (Exception ex) { LOG.error(ex.getMessage(), ex); } } this._pkcs11Provider = null; this._keyStore = null; } @Override public List<DSSPrivateKeyEntry> getKeys() throws DSSException { final List<DSSPrivateKeyEntry> list = new ArrayList<DSSPrivateKeyEntry>(); try { final KeyStore keyStore = getKeyStore(); final Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { final String alias = aliases.nextElement(); if (keyStore.isKeyEntry(alias)) { final PrivateKeyEntry entry = (PrivateKeyEntry) keyStore.getEntry(alias, null); list.add(new KSPrivateKeyEntry(entry)); } } } catch (Exception e) { throw new DSSException("Can't initialize Sun PKCS#11 security " + "provider. Reason: " + getCauseMessage(e), e); } return list; } }