/****************************************************************************
* Copyright (C) 2013 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.crypto.common.sal;
import iso.std.iso_iec._24727.tech.schema.ACLList;
import iso.std.iso_iec._24727.tech.schema.ACLListResponse;
import iso.std.iso_iec._24727.tech.schema.AccessRuleType;
import iso.std.iso_iec._24727.tech.schema.ActionNameType;
import iso.std.iso_iec._24727.tech.schema.CardApplicationList;
import iso.std.iso_iec._24727.tech.schema.CardApplicationListResponse;
import iso.std.iso_iec._24727.tech.schema.CardApplicationListResponse.CardApplicationNameList;
import iso.std.iso_iec._24727.tech.schema.CertificateRefType;
import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType;
import iso.std.iso_iec._24727.tech.schema.DIDGet;
import iso.std.iso_iec._24727.tech.schema.DIDGetResponse;
import iso.std.iso_iec._24727.tech.schema.DIDList;
import iso.std.iso_iec._24727.tech.schema.DIDListResponse;
import iso.std.iso_iec._24727.tech.schema.DIDQualifierType;
import iso.std.iso_iec._24727.tech.schema.DIDScopeType;
import iso.std.iso_iec._24727.tech.schema.NamedDataServiceActionName;
import iso.std.iso_iec._24727.tech.schema.TargetNameType;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.openecard.bouncycastle.asn1.x509.Certificate;
import org.openecard.bouncycastle.crypto.tls.CertificateRequest;
import org.openecard.common.WSHelper;
import org.openecard.common.WSHelper.WSException;
import org.openecard.common.interfaces.Dispatcher;
import org.openecard.common.interfaces.DispatcherException;
import org.openecard.common.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class that helps determine a DID for signature creation.
* The class is instantiated with a handle to a specific card in the system. Afterwards it can look for DIDs with
* different search strategies.
*
* @author Tobias Wich <tobias.wich@ecsec.de>
* @author Dirk Petrautzki <dirk.petrautzki@hs-coburg.de>
*/
public class GenericCryptoSignerFinder {
private static final Logger logger = LoggerFactory.getLogger(GenericCryptoSignerFinder.class);
private static final String OID_PKCS_1 = "urn:oid:1.2.840.113549.1.1";
private static final String OID_GENERIC_CRYPTO = "urn:oid:1.3.162.15480.3.0.25";
private static final String COMPUTE_SIGNATURE = "Compute-signature";
private final Dispatcher dispatcher;
private final ConnectionHandleType handle;
private boolean filterAlwaysReadable;
public GenericCryptoSignerFinder(@Nonnull Dispatcher dispatcher, @Nonnull ConnectionHandleType handle, boolean filterAlwaysReadable) {
this.filterAlwaysReadable = filterAlwaysReadable;
this.dispatcher = dispatcher;
this.handle = WSHelper.copyHandle(handle);
}
@Nonnull
public GenericCryptoSigner findDid(@Nonnull String didName) throws CredentialNotFound {
throw new UnsupportedOperationException("Not implemented yet");
}
@Nonnull
public GenericCryptoSigner findDid(@Nonnull String didName, @Nullable String algorithmUri)
throws CredentialNotFound {
throw new UnsupportedOperationException("Not implemented yet");
}
@Nonnull
public GenericCryptoSigner findFirstMatching(@Nonnull Certificate[] caChain) {
throw new UnsupportedOperationException("Not implemented yet");
}
@Nonnull
public GenericCryptoSigner findFirstMatching(@Nonnull org.openecard.bouncycastle.crypto.tls.Certificate caChain)
throws CredentialNotFound {
throw new UnsupportedOperationException("Not implemented yet");
}
@Nonnull
public GenericCryptoSigner findFirstMatching(@Nonnull java.security.cert.Certificate[] caChain)
throws CredentialNotFound {
throw new UnsupportedOperationException("Not implemented yet");
}
@Nonnull
public GenericCryptoSigner findFirstMatching(@Nonnull CertificateRequest cr)
throws CredentialNotFound {
List<Pair<String, byte[]>> result = findDID(dispatcher, handle);
if (result.isEmpty()) {
throw new CredentialNotFound("No suitable DID found.");
}
// TODO check remaining DIDs to match CertificateRequest
Pair<String, byte[]> firstResult = result.get(0);
handle.setCardApplication(firstResult.p2);
return new GenericCryptoSigner(dispatcher, handle, firstResult.p1);
}
// TODO: add more useful search functions
private List<Pair<String, byte[]>> findDID(Dispatcher dispatcher, ConnectionHandleType handle) {
List<Pair<String, byte[]>> result = new ArrayList<Pair<String, byte[]>>();
// copy handle to be safe from spaghetti code
handle = WSHelper.copyHandle(handle);
try {
CardApplicationList listReq = new CardApplicationList();
handle.setCardApplication(null);
listReq.setConnectionHandle(handle);
CardApplicationListResponse listRes = (CardApplicationListResponse) dispatcher.deliver(listReq);
WSHelper.checkResult(listRes);
CardApplicationNameList cardApplicationNameList = listRes.getCardApplicationNameList();
List<byte[]> cardApplicationName = cardApplicationNameList.getCardApplicationName();
for (byte[] appIdentifier : cardApplicationName) {
handle.setCardApplication(appIdentifier);
List<String> didNamesList = getSignatureCapableDIDs(dispatcher, handle);
didNamesList = filterTLSCapableDIDs(dispatcher, handle, didNamesList);
if (filterAlwaysReadable) {
didNamesList = filterAlwaysReadable(dispatcher, handle, didNamesList);
}
for (String didName : didNamesList) {
result.add(new Pair<String, byte[]>(didName, appIdentifier));
}
}
} catch (InvocationTargetException e) {
logger.error("Searching for DID failed", e);
} catch (DispatcherException e) {
logger.error("Searching for DID failed", e);
} catch (WSException e) {
logger.error("Searching for DID failed", e);
}
return result;
}
private List<String> filterTLSCapableDIDs(Dispatcher dispatcher, ConnectionHandleType handle,
List<String> didNames) throws DispatcherException, InvocationTargetException {
List<String> remainingDIDs = new ArrayList<String>();
for (String didName : didNames) {
DIDGet didGet = new DIDGet();
didGet.setConnectionHandle(handle);
didGet.setDIDName(didName);
DIDGetResponse didGetResponse = (DIDGetResponse) dispatcher.deliver(didGet);
CryptoMarkerType cryptoMarker = new CryptoMarkerType(didGetResponse.getDIDStructure().getDIDMarker());
String algorithm = cryptoMarker.getAlgorithmInfo().getAlgorithmIdentifier().getAlgorithm();
if (algorithm.equals(OID_PKCS_1)) {
logger.debug("{} is usable for TLS signatures.", didName);
remainingDIDs.add(didName);
} else {
logger.debug("{} is not usable for TLS signatures.", didName);
}
}
return remainingDIDs;
}
private List<String> filterAlwaysReadable(Dispatcher dispatcher, ConnectionHandleType handle, List<String> didNames)
throws DispatcherException, InvocationTargetException, WSException {
List<String> remainingDIDs = new ArrayList<String>();
for (String didName : didNames) {
// perform DIDGet for this DID
DIDGet didGet = new DIDGet();
didGet.setConnectionHandle(handle);
didGet.setDIDName(didName);
didGet.setDIDScope(DIDScopeType.LOCAL);
DIDGetResponse didGetResponse = (DIDGetResponse) dispatcher.deliver(didGet);
WSHelper.checkResult(didGetResponse);
// get the certificate data set name from crypto marker
CryptoMarkerType cryptoMarker = new CryptoMarkerType(didGetResponse.getDIDStructure().getDIDMarker());
CertificateRefType certificateRef = cryptoMarker.getCertificateRef();
String certDataSetName = certificateRef.getDataSetName();
// get the ACLList for the certificate data set
ACLList acllist = new ACLList();
acllist.setConnectionHandle(handle);
TargetNameType value = new TargetNameType();
value.setDataSetName(certDataSetName);
acllist.setTargetName(value);
ACLListResponse aclListResponse = (ACLListResponse) dispatcher.deliver(acllist);
WSHelper.checkResult(aclListResponse);
// check if it's always readable
for (AccessRuleType accessRule : aclListResponse.getTargetACL().getAccessRule()) {
if (accessRule.getCardApplicationServiceName().equals("NamedDataService")) {
ActionNameType action = accessRule.getAction();
NamedDataServiceActionName namedDataServiceAction = action.getNamedDataServiceAction();
if (namedDataServiceAction.equals(NamedDataServiceActionName.DSI_READ)) {
if (accessRule.getSecurityCondition().isAlways()) {
logger.debug("Certificate is always readable.");
remainingDIDs.add(didName);
} else {
logger.debug("Certificate needs did authentication to be readable.");
}
}
}
}
}
return remainingDIDs;
}
/**
* Get a list of DIDs suitable for generic cryptography signature creation.
*
* @param dispatcher
* @param handle
* @return Maybe empty list of DIDs
* @throws DispatcherException
* @throws InvocationTargetException
* @throws WSException
*/
private List<String> getSignatureCapableDIDs(Dispatcher dispatcher, ConnectionHandleType handle)
throws DispatcherException, InvocationTargetException, WSException {
DIDList didList = new DIDList();
didList.setConnectionHandle(handle);
DIDQualifierType filter = new DIDQualifierType();
filter.setApplicationFunction(COMPUTE_SIGNATURE);
filter.setObjectIdentifier(OID_GENERIC_CRYPTO);
filter.setApplicationIdentifier(handle.getCardApplication());
didList.setFilter(filter);
DIDListResponse didListResponse = (DIDListResponse) dispatcher.deliver(didList);
WSHelper.checkResult(didListResponse);
List<String> didNames = didListResponse.getDIDNameList().getDIDName();
return didNames;
}
}