/* * 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.lang.reflect.Field; import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.ProtectionParameter; import java.security.KeyStoreSpi; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.ec.markt.dss.exception.DSSException; /** * Class holding all MS CAPI API access logic. * * @version $Revision$ - $Date$ */ public class MSCAPISignatureToken extends AbstractSignatureTokenConnection { private static final Logger LOG = LoggerFactory.getLogger(MSCAPISignatureToken.class); private static class CallbackPasswordProtection extends KeyStore.PasswordProtection { PasswordInputCallback passwordCallback; public CallbackPasswordProtection(PasswordInputCallback callback) { super(null); this.passwordCallback = callback; } @Override public synchronized char[] getPassword() { if (passwordCallback == null) { throw new RuntimeException("MSCAPI: No callback provided for entering the PIN/password"); } char[] password = passwordCallback.getPassword(); return password; } } @Override public void close() { } /** * This method is a workaround for scenarios when multiple entries have the same alias. Since the alias is the only "official" * way of retrieving an entry, only the first entry with a given alias is accessible. * See: https://joinup.ec.europa.eu/software/sd-dss/issue/problem-possible-keystore-aliases-collision-when-using-mscapi * * @param keyStore the key store to fix */ private static void _fixAliases(KeyStore keyStore) { Field field; KeyStoreSpi keyStoreVeritable; try { field = keyStore.getClass().getDeclaredField("keyStoreSpi"); field.setAccessible(true); keyStoreVeritable = (KeyStoreSpi) field.get(keyStore); if ("sun.security.mscapi.KeyStore$MY".equals(keyStoreVeritable.getClass().getName())) { Collection<?> entries; String alias, hashCode; X509Certificate[] certificates; field = keyStoreVeritable.getClass().getEnclosingClass().getDeclaredField("entries"); field.setAccessible(true); entries = (Collection<?>) field.get(keyStoreVeritable); for (Object entry : entries) { field = entry.getClass().getDeclaredField("certChain"); field.setAccessible(true); certificates = (X509Certificate[]) field.get(entry); hashCode = Integer.toString(certificates[0].hashCode()); field = entry.getClass().getDeclaredField("alias"); field.setAccessible(true); alias = (String) field.get(entry); if (!alias.equals(hashCode)) { field.set(entry, alias.concat(" - ").concat(hashCode)); } } } } catch (Exception exception) { LOG.error(exception.getMessage(), exception); } } @Override public List<DSSPrivateKeyEntry> getKeys() throws DSSException { List<DSSPrivateKeyEntry> list = new ArrayList<DSSPrivateKeyEntry>(); try { ProtectionParameter protectionParameter = new CallbackPasswordProtection(new PrefilledPasswordCallback("nimp".toCharArray())); KeyStore keyStore = KeyStore.getInstance("Windows-MY"); keyStore.load(null, null); _fixAliases(keyStore); Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (keyStore.isKeyEntry(alias)) { PrivateKeyEntry entry = (PrivateKeyEntry) keyStore.getEntry(alias, protectionParameter); list.add(new KSPrivateKeyEntry(entry)); } } } catch (Exception e) { throw new DSSException(e); } return list; } }