/*
* Copyright 2015 Datentechnik Innovation GmbH and Prime Sign GmbH, Austria
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* http://www.osor.eu/eupl/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
package at.gv.egiz.bku.slcommands.impl;
import iaik.asn1.DerCoder;
import iaik.asn1.INTEGER;
import iaik.asn1.SEQUENCE;
import iaik.asn1.structures.AlgorithmID;
import iaik.cms.CMSException;
import iaik.cms.CMSSignatureException;
import iaik.utils.Util;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import at.buergerkarte.namespaces.securitylayer._1_2_3.BulkRequestType;
import at.buergerkarte.namespaces.securitylayer._1_2_3.BulkRequestType.CreateSignatureRequest;
import at.buergerkarte.namespaces.securitylayer._1_2_3.CreateCMSSignatureRequestType;
import at.buergerkarte.namespaces.securitylayer._1_2_3.ExcludedByteRangeType;
import at.gv.egiz.bku.conf.MoccaConfigurationFacade;
import at.gv.egiz.bku.slcommands.BulkCommand;
import at.gv.egiz.bku.slcommands.SLCommandContext;
import at.gv.egiz.bku.slcommands.SLResult;
import at.gv.egiz.bku.slcommands.impl.cms.BulkCollectionSecurityProvider;
import at.gv.egiz.bku.slcommands.impl.cms.BulkSignature;
import at.gv.egiz.bku.slcommands.impl.cms.BulkSignatureInfo;
import at.gv.egiz.bku.slcommands.impl.cms.CMSHashDataInput;
import at.gv.egiz.bku.slcommands.impl.xsect.STALSignatureException;
import at.gv.egiz.bku.slexceptions.SLCommandException;
import at.gv.egiz.bku.slexceptions.SLException;
import at.gv.egiz.bku.slexceptions.SLRequestException;
import at.gv.egiz.bku.slexceptions.SLViewerException;
import at.gv.egiz.stal.BulkSignRequest;
import at.gv.egiz.stal.BulkSignResponse;
import at.gv.egiz.stal.ErrorResponse;
import at.gv.egiz.stal.HashDataInput;
import at.gv.egiz.stal.InfoboxReadRequest;
import at.gv.egiz.stal.STALRequest;
import at.gv.egiz.stal.STALResponse;
import at.gv.egiz.stal.SignRequest;
import at.gv.egiz.stal.SignRequest.SignedInfo;
/**
* This class implements the security layer command BulkRequest
.
*
* @author szoescher
*/
public class BulkCommandImpl extends SLCommandImpl implements BulkCommand {
private final static String ID_ECSIGTYPE = "1.2.840.10045.4";
/**
* Logging facility.
*/
private final static Logger log = LoggerFactory.getLogger(BulkCommandImpl.class);
/**
* The signing certificate.
*/
protected X509Certificate signingCertificate;
/**
* The keybox identifier of the key used for signing.
*/
protected String keyboxIdentifier;
/**
* The configuration facade used to access the MOCCA configuration.
*/
private ConfigurationFacade configurationFacade = new ConfigurationFacade();
private class ConfigurationFacade implements MoccaConfigurationFacade {
private Configuration configuration;
public static final String USE_STRONG_HASH = "UseStrongHash";
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
public boolean getUseStrongHash() {
return configuration.getBoolean(USE_STRONG_HASH, true);
}
}
@Override
public String getName() {
return "BulkRequest";
}
public void setConfiguration(Configuration configuration) {
configurationFacade.setConfiguration(configuration);
}
@Override
public SLResult execute(SLCommandContext commandContext) {
List signatures = new LinkedList();
try {
List signatureRequests = getRequestValue().getCreateSignatureRequest();
List requestIds = new LinkedList();
if (signatureRequests != null && signatureRequests.size() != 0) {
BulkCollectionSecurityProvider securityProvider = new BulkCollectionSecurityProvider();
log.debug("get keyboxIdentifier from BulkSingatureRequest");
keyboxIdentifier = setKeyboxIdentifier(signatureRequests);
log.info("Requesting signing certificate.");
signingCertificate = requestSigningCertificate(keyboxIdentifier, commandContext);
log.debug("Got signing certificate. {}", signingCertificate);
for (int i=0; i signBulkRequest(List bulkSignatureInfo, SLCommandContext commandContext,
List signatures) throws SLCommandException, SLRequestException {
try {
List signatureValues;
BulkSignRequest signRequest = getSTALSignRequest(bulkSignatureInfo);
// send BulkStalRequest
List responses = commandContext.getSTAL().handleRequest(
Collections.singletonList((STALRequest) signRequest));
if (responses == null || responses.size() != 1) {
throw new SignatureException("Failed to access STAL.");
}
STALResponse response = responses.get(0);
// setSignatureValues from STALResponse
if (response instanceof BulkSignResponse) {
BulkSignResponse bulkSignatureResponse = ((BulkSignResponse) response);
signatureValues = new LinkedList();
for (int i = 0; i < bulkSignatureResponse.getSignResponse().size(); i++) {
byte[] sig = ((BulkSignResponse) response).getSignResponse().get(i).getSignatureValue();
log.debug("Got signature response: " + Util.toBase64String(sig));
signatures.get(i).getSignerInfo()
.setSignatureValue(wrapSignatureValue(sig, bulkSignatureInfo.get(i).getSignatureAlgorithm()));
signatureValues.add(signatures.get(i).getEncoded());
}
return signatureValues;
} else if (response instanceof ErrorResponse) {
ErrorResponse err = (ErrorResponse) response;
log.debug("Error signing bulk request. Error response code: " + err.getErrorCode() + " (" + err.getErrorMessage() + ").");
throw new SLCommandException(err.getErrorCode());
}
} catch (SignatureException e) {
log.error("Error creating CMSSignature", e);
throw new SLCommandException(4000);
} catch (CMSException e) {
log.error("Error creating CMSSignature", e);
}
return null;
}
private String setKeyboxIdentifier(List signatureRequests) throws SLCommandException {
String keyboxIdentifier = null;
for (CreateSignatureRequest request : signatureRequests) {
if (request.getCreateCMSSignatureRequest() != null) {
if (keyboxIdentifier == null) {
keyboxIdentifier = request.getCreateCMSSignatureRequest().getKeyboxIdentifier();
} else {
if (request.getCreateCMSSignatureRequest().getKeyboxIdentifier() == null) {
log.error("No keyboxIdentifier has been specified for this signature request.");
throw new SLCommandException(3003);
} else if (!request.getCreateCMSSignatureRequest().getKeyboxIdentifier().equals(keyboxIdentifier)) {
log.error("Error creating bulk signature. The bulkSignature value has to be the same fo all signature requests.");
throw new SLCommandException(3003);
}
}
}
}
return keyboxIdentifier;
}
private BulkSignature prepareCMSSignatureRequests(BulkCollectionSecurityProvider securityProvieder,
CreateCMSSignatureRequestType request, SLCommandContext commandContext) throws SLCommandException,
SLRequestException, SLViewerException {
BulkSignature signature;
// prepare the CMSSignature for signing
log.debug("Preparing CMS signature.");
signature = prepareCMSSignature(request, commandContext);
try {
// update securityProvieder with parameters of the given signature
securityProvieder.updateBulkCollectionSecurityProvider(keyboxIdentifier, signature.getHashDataInput(),
signature.getExcludedByteRange());
// prepare the CMSSignatures of the Bulk Request
log.debug("Signing CMS signature.");
return prepareStalRequest(securityProvieder, signature, commandContext);
} catch (Exception e) {
log.error("Error creating CMS Signature.", e);
throw new SLCommandException(4000);
}
}
private BulkSignature prepareCMSSignature(CreateCMSSignatureRequestType request, SLCommandContext commandContext)
throws SLCommandException, SLRequestException {
// DataObject, SigningCertificate, SigningTime
Date signingTime = new Date();
try {
return new BulkSignature(
request.getDataObject() != null ? request.getDataObject() : request.getReferenceObject(),
request.getStructure(), signingCertificate, signingTime, commandContext.getURLDereferencer(),
configurationFacade.getUseStrongHash());
} catch (SLCommandException e) {
log.error("Error creating CMS Signature.", e);
throw e;
} catch (InvalidParameterException e) {
log.error("Error creating CMS Signature.", e);
throw new SLCommandException(3004);
} catch (Exception e) {
log.error("Error creating CMS Signature.", e);
throw new SLCommandException(4000);
}
}
private BulkSignature prepareStalRequest(BulkCollectionSecurityProvider securityProvieder, BulkSignature signature,
SLCommandContext commandContext) throws SLCommandException, SLViewerException {
try {
signature.sign(securityProvieder, commandContext.getSTAL(), keyboxIdentifier);
return signature;
} catch (CMSException e) {
log.error("Error creating CMSSignature", e);
throw new SLCommandException(4000);
} catch (CMSSignatureException e) {
log.error("Error creating CMSSignature", e);
throw new SLCommandException(4000);
}
}
private X509Certificate requestSigningCertificate(String keyboxIdentifier, SLCommandContext commandContext)
throws SLCommandException {
InfoboxReadRequest stalRequest = new InfoboxReadRequest();
stalRequest.setInfoboxIdentifier(keyboxIdentifier);
STALHelper stalHelper = new STALHelper(commandContext.getSTAL());
stalHelper.transmitSTALRequest(Collections.singletonList((STALRequest) stalRequest));
List certificates = stalHelper.getCertificatesFromResponses();
if (certificates == null || certificates.size() != 1) {
log.info("Got an unexpected number of certificates from STAL.");
throw new SLCommandException(4000);
}
return signingCertificate = certificates.get(0);
}
private static BulkSignRequest getSTALSignRequest(List bulkSignatureInfo) {
BulkSignRequest bulkSignRequest = new BulkSignRequest();
for (int i = 0; i< bulkSignatureInfo.size(); i++) {
BulkSignatureInfo signatureInfo = bulkSignatureInfo.get(i);
SignRequest signRequest = new SignRequest();
signRequest.setKeyIdentifier(signatureInfo.getKeyboxIdentifier());
log.debug("SignedAttributes: " + Util.toBase64String(signatureInfo.getSignedAttributes()));
SignedInfo signedInfo = new SignedInfo();
signedInfo.setValue(signatureInfo.getSignedAttributes());
signedInfo.setIsCMSSignedAttributes(true);
signRequest.setSignedInfo(signedInfo);
log.info("set displayName for Request {}", signatureInfo.getHashDataInput().get(0).getFilename());
signRequest.setDisplayName(signatureInfo.getHashDataInput().get(0).getFilename());
signRequest.setMimeType(signatureInfo.getHashDataInput().get(0).getMimeType());
signRequest.setSignatureMethod(signatureInfo.getSignatureMethod());
signRequest.setDigestMethod(signatureInfo.getDigestMethod());
signRequest.setHashDataInput(signatureInfo.getHashDataInput());
ExcludedByteRangeType excludedByteRange = signatureInfo.getExcludedByteRange();
if (excludedByteRange != null) {
SignRequest.ExcludedByteRange ebr = new SignRequest.ExcludedByteRange();
ebr.setFrom(excludedByteRange.getFrom());
ebr.setTo(excludedByteRange.getTo());
signRequest.setExcludedByteRange(ebr);
}
bulkSignRequest.getSignRequests().add(signRequest);
}
return bulkSignRequest;
}
private static byte[] wrapSignatureValue(byte[] sig, AlgorithmID sigAlgorithmID) {
String id = sigAlgorithmID.getAlgorithm().getID();
if (id.startsWith(ID_ECSIGTYPE)) // X9.62 Format ECDSA signatures
{
// Wrap r and s in ASN.1 SEQUENCE
byte[] r = Arrays.copyOfRange(sig, 0, sig.length / 2);
byte[] s = Arrays.copyOfRange(sig, sig.length / 2, sig.length);
SEQUENCE sigS = new SEQUENCE();
sigS.addComponent(new INTEGER(new BigInteger(1, r)));
sigS.addComponent(new INTEGER(new BigInteger(1, s)));
return DerCoder.encode(sigS);
} else
return sig;
}
}