/** * DSS - Digital Signature Services * Copyright (C) 2015 European Commission, provided under the CEF programme * * This file is part of the "DSS - Digital Signature Services" project. * * This library 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. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package eu.europa.esig.dss.token.mocca; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import javax.smartcardio.Card; import javax.smartcardio.CardTerminal; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.DERSequence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.smcc.CardNotSupportedException; import at.gv.egiz.smcc.SignatureCard; import at.gv.egiz.smcc.SignatureCard.KeyboxName; import at.gv.egiz.smcc.SignatureCardFactory; import at.gv.egiz.smcc.util.SmartCardIO; import eu.europa.esig.dss.DSSASN1Utils; import eu.europa.esig.dss.DSSException; import eu.europa.esig.dss.DigestAlgorithm; import eu.europa.esig.dss.EncryptionAlgorithm; import eu.europa.esig.dss.SignatureAlgorithm; import eu.europa.esig.dss.SignatureValue; import eu.europa.esig.dss.ToBeSigned; import eu.europa.esig.dss.token.DSSPrivateKeyEntry; import eu.europa.esig.dss.token.PasswordInputCallback; import eu.europa.esig.dss.token.SignatureTokenConnection; /** * */ @SuppressWarnings("restriction") public class MOCCASignatureTokenConnection implements SignatureTokenConnection { private static final Logger LOG = LoggerFactory.getLogger(MOCCASignatureTokenConnection.class); private PINGUIAdapter callback; private List<SignatureCard> _signatureCards; /** * Use this constructor when the signature algorithm is not known before the connection is opened. You must set the * SignatureAlgorithm property of the key after the connection has been opened (you can get the SignatureAlgorithm * name from the key) * * @param callback * provides the PIN */ public MOCCASignatureTokenConnection(PasswordInputCallback callback) { this.callback = new PINGUIAdapter(callback); } public void set_signatureCards(List<SignatureCard> _signatureCards) { this._signatureCards = _signatureCards; } @Override public void close() { if (_signatureCards != null) { for (SignatureCard c : _signatureCards) { c.disconnect(true); } _signatureCards.clear(); _signatureCards = null; } } private List<SignatureCard> getSignatureCards() { if (_signatureCards == null) { _signatureCards = new ArrayList<SignatureCard>(); SmartCardIO io = new SmartCardIO(); SignatureCardFactory factory = SignatureCardFactory.getInstance(); for (Entry<CardTerminal, Card> entry : io.getCards().entrySet()) { try { _signatureCards.add(factory.createSignatureCard(entry.getValue(), entry.getKey())); } catch (CardNotSupportedException e) { // just log the error - MOCCA tries to connect to all cards and we may have an MSCAPI or PKCS11 also // inserted. LOG.info(e.getMessage()); } } } return _signatureCards; } @Override public List<DSSPrivateKeyEntry> getKeys() throws DSSException { List<DSSPrivateKeyEntry> list = getKeysSeveralCards(); if (list.size() == 0) { throw new DSSException("Cannot retrieve keys from the card!"); } return list; } private List<DSSPrivateKeyEntry> getKeysSeveralCards() throws DSSException { final List<DSSPrivateKeyEntry> list = new ArrayList<DSSPrivateKeyEntry>(); final List<SignatureCard> cardList = getSignatureCards(); int index = 0; for (SignatureCard sc : cardList) { try { final byte[] data = sc.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR, callback); if (data != null) { list.add(new MOCCAPrivateKeyEntry(data, KeyboxName.SECURE_SIGNATURE_KEYPAIR, index, sc.getCard().getATR().getBytes())); } } catch (Exception e) { LOG.error(e.getMessage(), e); } try { final byte[] data = sc.getCertificate(KeyboxName.CERTIFIED_KEYPAIR, callback); if (data != null) { list.add(new MOCCAPrivateKeyEntry(data, KeyboxName.CERTIFIED_KEYPAIR, index, sc.getCard().getATR().getBytes())); } } catch (Exception e) { LOG.error(e.getMessage(), e); } index++; } return list; } @Override public SignatureValue sign(ToBeSigned toBeSigned, DigestAlgorithm digestAlgorithm, DSSPrivateKeyEntry keyEntry) throws DSSException { final InputStream inputStream = new ByteArrayInputStream(toBeSigned.getBytes()); if (!(keyEntry instanceof MOCCAPrivateKeyEntry)) { throw new DSSException("Unsupported DSSPrivateKeyEntry instance " + keyEntry.getClass() + " / Must be MOCCAPrivateKeyEntry."); } final MOCCAPrivateKeyEntry moccaKey = (MOCCAPrivateKeyEntry) keyEntry; if (_signatureCards == null) { throw new IllegalStateException("The cards have not been initialised"); } // TODO Bob:20130619 This is not completely true, it is true only for the last card. The signing certificate // should be checked. if (moccaKey.getPos() > (_signatureCards.size() - 1)) { throw new IllegalStateException("Card was removed or disconnected " + moccaKey.getPos() + " " + _signatureCards.size()); } final SignatureCard signatureCard = _signatureCards.get(moccaKey.getPos()); final EncryptionAlgorithm encryptionAlgo = moccaKey.getEncryptionAlgorithm(); final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getAlgorithm(encryptionAlgo, digestAlgorithm); LOG.info("MOCCA>>>Signature algorithm: " + signatureAlgorithm.getJCEId()); try { final KeyboxName keyboxName = moccaKey.getKeyboxName(); byte[] signedData = signatureCard.createSignature(inputStream, keyboxName, callback, signatureAlgorithm.getXMLId()); if (EncryptionAlgorithm.ECDSA.equals(encryptionAlgo)) { signedData = encode(signedData); } SignatureValue value = new SignatureValue(); value.setAlgorithm(signatureAlgorithm); value.setValue(signedData); return value; } catch (Exception e) { throw new DSSException(e); } } /** * The ECDSA_SIG structure consists of two BIGNUMs for the r and s value of a ECDSA signature (see X9.62 or FIPS * 186-2).<br> * This encoding is not implemented at the level of MOCCA! * * @param signedStream * @return * @throws eu.europa.esig.dss.DSSException */ private static byte[] encode(byte[] signedStream) throws DSSException { final int half = signedStream.length / 2; final byte[] firstPart = new byte[half]; final byte[] secondPart = new byte[half]; System.arraycopy(signedStream, 0, firstPart, 0, half); System.arraycopy(signedStream, half, secondPart, 0, half); final BigInteger r = new BigInteger(1, firstPart); final BigInteger s = new BigInteger(1, secondPart); final ASN1EncodableVector v = new ASN1EncodableVector(); v.add(new ASN1Integer(r)); v.add(new ASN1Integer(s)); return DSSASN1Utils.getDEREncoded(new DERSequence(v)); } public int getRetries() { return callback.getRetries(); } }