eu.europa.ec.markt.dss.mocca.MOCCASignatureTokenConnection.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.ec.markt.dss.mocca.MOCCASignatureTokenConnection.java

Source

/*
 * DSS - Digital Signature Services
 *
 * Copyright (C) 2011 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
 *
 * Developed by: 2011 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.mocca;

import java.io.IOException;
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.DERInteger;
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.SignatureCardException;
import at.gv.egiz.smcc.SignatureCardFactory;
import at.gv.egiz.smcc.util.SmartCardIO;
import eu.europa.ec.markt.dss.DSSASN1Utils;
import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.DigestAlgorithm;
import eu.europa.ec.markt.dss.EncryptionAlgorithm;
import eu.europa.ec.markt.dss.SignatureAlgorithm;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.signature.token.DSSPrivateKeyEntry;
import eu.europa.ec.markt.dss.signature.token.PasswordInputCallback;
import eu.europa.ec.markt.dss.signature.token.SignatureTokenConnection;

/**
 * @author bielecro
 */
@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);
    }

    @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 byte[] sign(byte[] bytes, DigestAlgorithm digestAlgo, DSSPrivateKeyEntry keyEntry) throws DSSException {

        final InputStream inputStream = DSSUtils.toInputStream(bytes);
        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, digestAlgo);

        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);
            }
            return signedData;
        } catch (SignatureCardException e) {
            throw new DSSException(e);
        } catch (InterruptedException e) {
            throw new DSSException(e);
        } catch (IOException 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.ec.markt.dss.exception.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 DERInteger(r));
        v.add(new DERInteger(s));

        return DSSASN1Utils.getDEREncoded(new DERSequence(v));
    }

    public int getRetries() {
        return callback.getRetries();
    }
}