diff options
author | tkellner <tkellner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2013-11-28 13:23:09 +0000 |
---|---|---|
committer | tkellner <tkellner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2013-11-28 13:23:09 +0000 |
commit | 624dc3a0b6ef39948b9e78841ef7f75f27fee8da (patch) | |
tree | bc729d9f6e0f8604a4b48b375312886dccdcc223 /bkucommon | |
parent | 4a4302351610a5bcc24cd0e296a40232228c1b5f (diff) | |
download | mocca-624dc3a0b6ef39948b9e78841ef7f75f27fee8da.tar.gz mocca-624dc3a0b6ef39948b9e78841ef7f75f27fee8da.tar.bz2 mocca-624dc3a0b6ef39948b9e78841ef7f75f27fee8da.zip |
Implement CreateCMSSignatureRequest
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1234 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'bkucommon')
10 files changed, 806 insertions, 0 deletions
diff --git a/bkucommon/pom.xml b/bkucommon/pom.xml index 96fe94f2..f6e0f0c6 100644 --- a/bkucommon/pom.xml +++ b/bkucommon/pom.xml @@ -91,6 +91,10 @@ </dependency> <dependency> <groupId>iaik</groupId> + <artifactId>iaik_cms</artifactId> + </dependency> + <dependency> + <groupId>iaik</groupId> <artifactId>iaik_pki</artifactId> <scope>compile</scope> </dependency> diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateCMSSignatureCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateCMSSignatureCommand.java new file mode 100644 index 00000000..a68c0125 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateCMSSignatureCommand.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013 by Graz University of Technology, Austria + * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint + * initiative of the Federal Chancellery Austria and Graz University of Technology. + * + * 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; + +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLRequestException; + +public interface CreateCMSSignatureCommand extends SLCommand { + + public void prepareCMSSignature(SLCommandContext commandContext) throws SLCommandException, SLRequestException; + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateCMSSignatureResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateCMSSignatureResult.java new file mode 100644 index 00000000..8f215843 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateCMSSignatureResult.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013 by Graz University of Technology, Austria + * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint + * initiative of the Federal Chancellery Austria and Graz University of Technology. + * + * 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; + +import org.w3c.dom.Element; + +public interface CreateCMSSignatureResult extends SLResult { + + public Element getContent(); + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureCommandFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureCommandFactory.java new file mode 100644 index 00000000..2a642f95 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureCommandFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright 2011 by Graz University of Technology, Austria + * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint + * initiative of the Federal Chancellery Austria and Graz University of Technology. + * + * 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 javax.xml.bind.JAXBElement; + +import at.gv.egiz.bku.slcommands.AbstractSLCommandFactory; +import at.gv.egiz.bku.slcommands.SLCommand; +import at.gv.egiz.bku.slexceptions.SLCommandException; + +public class CreateCMSSignatureCommandFactory extends AbstractSLCommandFactory { + + @Override + public SLCommand createSLCommand(JAXBElement<?> element) throws SLCommandException { + CreateCMSSignatureCommandImpl command = new CreateCMSSignatureCommandImpl(); + command.init(element); + command.setConfiguration(configuration); + return command; + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureCommandImpl.java new file mode 100644 index 00000000..4825351b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureCommandImpl.java @@ -0,0 +1,201 @@ +/* + * Copyright 2013 by Graz University of Technology, Austria + * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint + * initiative of the Federal Chancellery Austria and Graz University of Technology. + * + * 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.cms.CMSException; + +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.apache.commons.configuration.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.buergerkarte.namespaces.securitylayer._1_2_3.CreateCMSSignatureRequestType; +import at.gv.egiz.bku.conf.MoccaConfigurationFacade; +import at.gv.egiz.bku.slcommands.CreateCMSSignatureCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slcommands.impl.cms.Signature; +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.InfoboxReadRequest; +import at.gv.egiz.stal.STALRequest; + +/** + * This class implements the security layer command + * <code>CreateCMSSignatureRequest</code>. + * + * @author tkellner + */ +public class CreateCMSSignatureCommandImpl extends + SLCommandImpl<CreateCMSSignatureRequestType> implements + CreateCMSSignatureCommand { + + /** + * Logging facility. + */ + private final Logger log = LoggerFactory.getLogger(CreateCMSSignatureCommandImpl.class); + + /** + * The signing certificate. + */ + protected X509Certificate signingCertificate; + + /** + * The keybox identifier of the key used for signing. + */ + protected String keyboxIdentifier; + + /** + * The to-be signed signature. + */ + protected Signature signature; + + /** + * 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); + } +} + + public void setConfiguration(Configuration configuration) { + configurationFacade.setConfiguration(configuration); + } + + @Override + public void prepareCMSSignature(SLCommandContext commandContext) throws SLCommandException, + SLRequestException { + + CreateCMSSignatureRequestType request = getRequestValue(); + + // DataObject, SigningCertificate, SigningTime + Date signingTime = new Date(); + try { + signature = new Signature(request.getDataObject(), signingCertificate, + signingTime, configurationFacade.getUseStrongHash()); + } catch (Exception e) { + log.error("Error creating CMS Signature.", e); + throw new SLCommandException(4000); + } + } + + /** + * Gets the signing certificate from STAL. + * @param commandContext TODO + * + * @throws SLCommandException + * if getting the singing certificate fails + */ + private void getSigningCertificate(SLCommandContext commandContext) throws SLCommandException { + + CreateCMSSignatureRequestType request = getRequestValue(); + keyboxIdentifier = request.getKeyboxIdentifier(); + + InfoboxReadRequest stalRequest = new InfoboxReadRequest(); + stalRequest.setInfoboxIdentifier(keyboxIdentifier); + + STALHelper stalHelper = new STALHelper(commandContext.getSTAL()); + + stalHelper.transmitSTALRequest(Collections.singletonList((STALRequest) stalRequest)); + List<X509Certificate> certificates = stalHelper.getCertificatesFromResponses(); + if (certificates == null || certificates.size() != 1) { + log.info("Got an unexpected number of certificates from STAL."); + throw new SLCommandException(4000); + } + signingCertificate = certificates.get(0); + + } + + /** + * Signs the signature. + * @param commandContext TODO + * @return the CMS signature + * @throws SLCommandException + * if signing the signature fails + * @throws SLViewerException + */ + private byte[] signCMSSignature(SLCommandContext commandContext) throws SLCommandException, SLViewerException { + + try { + return signature.sign(commandContext.getSTAL(), keyboxIdentifier); + } catch (CMSException e) { + log.error("Error creating CMSSignature", e); + throw new SLCommandException(4000); + } + } + + @Override + public SLResult execute(SLCommandContext commandContext) { + try { + + // get certificate in order to select appropriate algorithms for hashing + // and signing + log.info("Requesting signing certificate."); + getSigningCertificate(commandContext); + if (log.isDebugEnabled()) { + log.debug("Got signing certificate. {}", signingCertificate); + } else { + log.info("Got signing certificate."); + } + + // prepare the CMSSignature for signing + log.info("Preparing CMS signature."); + prepareCMSSignature(commandContext); + + // sign the CMSSignature + log.info("Signing CMS signature."); + byte[] sig = signCMSSignature(commandContext); + log.info("CMS signature signed."); + + return new CreateCMSSignatureResultImpl(sig); + + } catch (SLException e) { + return new ErrorResultImpl(e, commandContext.getLocale()); + } + } + + @Override + public String getName() { + return "CreateCMSSignatureRequest"; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureResultImpl.java new file mode 100644 index 00000000..66b24a82 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateCMSSignatureResultImpl.java @@ -0,0 +1,117 @@ +/* + * Copyright 2011 by Graz University of Technology, Austria + * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint + * initiative of the Federal Chancellery Austria and Graz University of Technology. + * + * 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 javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; +import javax.xml.transform.dom.DOMResult; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import at.buergerkarte.namespaces.securitylayer._1_2_3.CreateCMSSignatureResponseType; +import at.buergerkarte.namespaces.securitylayer._1_2_3.ObjectFactory; +import at.gv.egiz.bku.slcommands.CreateCMSSignatureResult; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; + +/** + * This implements the result of the security layer command <code>CreateCMSSignature</code>. + * + * @author tkellner + */ +public class CreateCMSSignatureResultImpl extends SLResultImpl implements CreateCMSSignatureResult { + + /** + * Logging facility. + */ + private final Logger log = LoggerFactory.getLogger(CreateCMSSignatureResultImpl.class); + + /** + * The CMSSignature data. + */ + protected byte[] signature; + + /** + * The CMSSignatureResponse. + */ + private Element content; + + /** + * Creates a new instance of this CreateCMSSignatureResultImpl with the given + * signature <code>signature</code>. + * + * @param document the signature document + * + * @throws NullPointerException if <code>document</code> is <code>null</code> + */ + public CreateCMSSignatureResultImpl(byte[] signature) { + super(); + + if (signature == null) + throw new NullPointerException("Argument 'signature' must not be null."); + this.signature = signature; + + marshallCreateCMSSignatureResponse(); + } + + /** + * Marshalls the <code>CreateCMSSignatureResponse</code>. + */ + private void marshallCreateCMSSignatureResponse() { + + ObjectFactory factory = new ObjectFactory(); + + CreateCMSSignatureResponseType createCreateCMSSignatureResponseType = factory.createCreateCMSSignatureResponseType(); + createCreateCMSSignatureResponseType.setCMSSignature(signature); + JAXBElement<CreateCMSSignatureResponseType> createCreateCMSSignatureResponse = factory.createCreateCMSSignatureResponse(createCreateCMSSignatureResponseType); + + DOMResult res = new DOMResult(); + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); + try { + marshaller.marshal(createCreateCMSSignatureResponse, res); + } catch (JAXBException e) { + log.error("Failed to marshall 'CreateCMSSignatureResponse'.", e); + throw new SLRuntimeException(e); + } + content = ((Document)res.getNode()).getDocumentElement(); + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(content, result, templates, fragment); + } + + @Override + public Element getContent() { + return content; + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALPrivateKey.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALPrivateKey.java new file mode 100644 index 00000000..8da52227 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALPrivateKey.java @@ -0,0 +1,34 @@ +package at.gv.egiz.bku.slcommands.impl.cms; + +import java.security.PrivateKey; + +/** + * Dummy PrivateKey implementation for CMS signing using STAL + * @author tkellner + */ +public class STALPrivateKey implements PrivateKey { + + private static final long serialVersionUID = 1L; + + private String algorithm; + + public STALPrivateKey(String algorithm) { + this.algorithm = algorithm; + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public byte[] getEncoded() { + throw new UnsupportedOperationException("STALPrivateKey does not support the getEncoded() method."); + } + + @Override + public String getFormat() { + return null; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALSecurityProvider.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALSecurityProvider.java new file mode 100644 index 00000000..437d29ef --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALSecurityProvider.java @@ -0,0 +1,76 @@ +package at.gv.egiz.bku.slcommands.impl.cms; + +import iaik.asn1.structures.AlgorithmID; +import iaik.cms.IaikProvider; +import iaik.utils.Util; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SignatureException; +import java.util.Collections; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.bku.slcommands.impl.xsect.STALSignatureException; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.STAL; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.SignRequest; +import at.gv.egiz.stal.SignResponse; + +public class STALSecurityProvider extends IaikProvider { + + private final Logger log = LoggerFactory.getLogger(STALSecurityProvider.class); + + private String keyboxIdentifier; + + private STAL stal; + + public STALSecurityProvider(STAL stal, String keyboxIdentifier) { + this.keyboxIdentifier = keyboxIdentifier; + this.stal = stal; + } + + /* (non-Javadoc) + * @see iaik.cms.IaikProvider#calculateSignatureFromSignedAttributes(iaik.asn1.structures.AlgorithmID, iaik.asn1.structures.AlgorithmID, java.security.PrivateKey, byte[]) + */ + @Override + public byte[] calculateSignatureFromSignedAttributes(AlgorithmID signatureAlgorithm, + AlgorithmID digestAlgorithm, PrivateKey privateKey, + byte[] signedAttributes) + throws SignatureException, InvalidKeyException, NoSuchAlgorithmException { + log.debug("calculateSignatureFromSignedAttributes: " + signatureAlgorithm + ", " + digestAlgorithm); + + SignRequest signRequest = new SignRequest(); + signRequest.setKeyIdentifier(keyboxIdentifier); + log.debug("SignedAttributes: " + Util.toBase64String(signedAttributes)); + signRequest.setSignedInfo(signedAttributes); + signRequest.setSignedInfoIsRawData(true); + signRequest.setSignatureMethod(privateKey.getAlgorithm()); + + log.debug("Sending STAL request"); + List<STALResponse> responses = + stal.handleRequest(Collections.singletonList((STALRequest) signRequest)); + + if (responses == null || responses.size() != 1) { + throw new SignatureException("Failed to access STAL."); + } + + STALResponse response = responses.get(0); + if (response instanceof SignResponse) { + log.debug("Got STAL response: " + Util.toBase64String(((SignResponse) response).getSignatureValue())); + return ((SignResponse) response).getSignatureValue(); + } else if (response instanceof ErrorResponse) { + ErrorResponse err = (ErrorResponse) response; + STALSignatureException se = new STALSignatureException(err.getErrorCode(), err.getErrorMessage()); + throw new SignatureException(se); + } else { + throw new SignatureException("Failed to access STAL."); + } + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/Signature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/Signature.java new file mode 100644 index 00000000..9f97e363 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/Signature.java @@ -0,0 +1,255 @@ +/* + * Copyright 2013 by Graz University of Technology, Austria + * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint + * initiative of the Federal Chancellery Austria and Graz University of Technology. + * + * 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.cms; + +import iaik.asn1.ASN1Object; +import iaik.asn1.CodingException; +import iaik.asn1.ObjectID; +import iaik.asn1.SEQUENCE; +import iaik.asn1.UTF8String; +import iaik.asn1.structures.AlgorithmID; +import iaik.asn1.structures.Attribute; +import iaik.asn1.structures.ChoiceOfTime; +import iaik.cms.CMSException; +import iaik.cms.CertificateIdentifier; +import iaik.cms.ContentInfo; +import iaik.cms.IssuerAndSerialNumber; +import iaik.cms.SignedData; +import iaik.cms.SignerInfo; +import iaik.security.ecc.interfaces.ECDSAParams; +import iaik.smime.ess.ESSCertID; +import iaik.smime.ess.ESSCertIDv2; +import iaik.x509.X509ExtensionException; + +import java.security.InvalidParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.buergerkarte.namespaces.securitylayer._1_2_3.CMSDataObjectRequiredMetaType; +import at.buergerkarte.namespaces.securitylayer._1_2_3.ExcludedByteRangeType; +import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory; +import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactoryImpl; +import at.gv.egiz.stal.STAL; + +/** + * This class represents a CMS-Signature as to be created by the + * security layer command <code>CreateCMSSignatureRequest</code>. + * + * @author tkellner + */ +public class Signature { + /** + * Logging facility. + */ + private final Logger log = LoggerFactory.getLogger(Signature.class); + + private SignedData signedData; + private SignerInfo signerInfo; + private AlgorithmID signatureAlgorithm; + private AlgorithmID digestAlgorithm; + private String signatureAlgorithmURI; + @SuppressWarnings("unused") + private String digestAlgorithmURI; + + public Signature(CMSDataObjectRequiredMetaType dataObject, + X509Certificate signingCertificate, Date signingTime, boolean useStrongHash) throws NoSuchAlgorithmException, CertificateEncodingException, CertificateException, X509ExtensionException, InvalidParameterException, CodingException { + byte[] data = getContent(dataObject); + this.signedData = new SignedData(data, SignedData.EXPLICIT); + setAlgorithmIDs(signingCertificate, useStrongHash); + createSignerInfo(signingCertificate); + setSignerCertificate(signingCertificate); + setAttributes(dataObject.getMetaInfo().getMimeType(), signingCertificate, signingTime); + } + + private void createSignerInfo(X509Certificate signingCertificate) throws CertificateEncodingException, CertificateException { + iaik.x509.X509Certificate sigcert = + new iaik.x509.X509Certificate(signingCertificate.getEncoded()); + CertificateIdentifier signerIdentifier = + new IssuerAndSerialNumber(sigcert); + PrivateKey privateKey = new STALPrivateKey(signatureAlgorithmURI); + signerInfo = new SignerInfo(signerIdentifier, digestAlgorithm, + signatureAlgorithm, privateKey); + } + + private void setSignerCertificate(X509Certificate signingCertificate) { + X509Certificate[] sigcerts = new X509Certificate[] { signingCertificate }; + signedData.addCertificates(sigcerts); + } + + private void setAttributes(String mimeType, X509Certificate signingCertificate, Date signingTime) throws CertificateException, NoSuchAlgorithmException, CodingException { + List<Attribute> attributes = new ArrayList<Attribute>(); + setMimeTypeAttrib(attributes, mimeType); + setContentTypeAttrib(attributes); + setSigningCertificateAttrib(attributes, signingCertificate); + setSigningTimeAttrib(attributes, signingTime); + Attribute[] attributeArray = attributes.toArray(new Attribute[attributes.size()]); + signerInfo.setSignedAttributes(attributeArray); + } + + private void setMimeTypeAttrib(List<Attribute> attributes, String mimeType) { + String oidStr = "0.4.0.1733.2.1"; + String name = "mime-type"; + ObjectID mimeTypeOID = new ObjectID(oidStr, name); + + Attribute mimeTypeAtt = new Attribute(mimeTypeOID, new ASN1Object[] {new UTF8String(mimeType)}); + attributes.add(mimeTypeAtt); + } + + private void setContentTypeAttrib(List<Attribute> attributes) { + Attribute contentType = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.cms_data}); + attributes.add(contentType); + } + + private void setSigningCertificateAttrib(List<Attribute> attributes, X509Certificate signingCertificate) throws CertificateException, NoSuchAlgorithmException, CodingException { + ObjectID id; + ASN1Object value = new SEQUENCE(); + if (digestAlgorithm.equals(AlgorithmID.sha1)) { + id = ObjectID.signingCertificate; + value.addComponent(new ESSCertID(signingCertificate, true).toASN1Object()); + } + else { + id = ObjectID.signingCertificateV2; + value.addComponent(new ESSCertIDv2(digestAlgorithm, signingCertificate, true).toASN1Object()); + } + ASN1Object signingCert = new SEQUENCE(); + signingCert.addComponent(value); + Attribute signingCertificateAttrib = new Attribute(id, new ASN1Object[] {signingCert}); + attributes.add(signingCertificateAttrib); + } + + private void setSigningTimeAttrib(List<Attribute> attributes, Date date) { + Attribute signingTime = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime(date).toASN1Object()}); + attributes.add(signingTime); + } + + private byte[] getContent(CMSDataObjectRequiredMetaType dataObject) throws InvalidParameterException { + byte[] data = dataObject.getContent().getBase64Content(); + ExcludedByteRangeType ebr = dataObject.getExcludedByteRange(); + if (ebr == null) + return data; + + int from = dataObject.getExcludedByteRange().getFrom().intValue(); + int to = dataObject.getExcludedByteRange().getTo().intValue(); + if (from > data.length || to > data.length || from > to) + throw new InvalidParameterException("ExcludeByteRange contains invalid data: [" + + from + "-" + to + "], Content length: " + data.length); + byte[] first = null; + byte[] second = null; + if (from > 0) + first = Arrays.copyOfRange(data, 0, from); + if ((to + 1) < data.length) + second = Arrays.copyOfRange(data, to + 1, data.length); + data = ArrayUtils.addAll(first, second); + log.debug("ExcludeByteRange [" + from + "-" + to + "], Content length: " + data.length); + return data; + } + + private void setSignerInfo() { + try { + signedData.addSignerInfo(signerInfo); + } catch (NoSuchAlgorithmException e) { + log.error("Error setting signer info", e); + } + } + + private void setAlgorithmIDs(X509Certificate signingCertificate, boolean useStrongHash) throws NoSuchAlgorithmException { + PublicKey publicKey = signingCertificate.getPublicKey(); + String algorithm = publicKey.getAlgorithm(); + AlgorithmMethodFactory amf = new AlgorithmMethodFactoryImpl(signingCertificate, useStrongHash); + signatureAlgorithmURI = amf.getSignatureAlgorithmURI(); + digestAlgorithmURI = amf.getDigestAlgorithmURI(); + + if ("DSA".equals(algorithm)) { + signatureAlgorithm = AlgorithmID.dsaWithSHA1; + } else if ("RSA".equals(algorithm)) { + + int keyLength = 0; + if (publicKey instanceof RSAPublicKey) { + keyLength = ((RSAPublicKey) publicKey).getModulus().bitLength(); + } + + if (useStrongHash && keyLength >= 2048) { + signatureAlgorithm = AlgorithmID.sha256WithRSAEncryption; + digestAlgorithm = AlgorithmID.sha256; + } else if (useStrongHash) { + signatureAlgorithm = AlgorithmID.rsaSignatureWithRipemd160; + digestAlgorithm = AlgorithmID.ripeMd160; + } else { + signatureAlgorithm = AlgorithmID.sha1WithRSAEncryption; + digestAlgorithm = AlgorithmID.sha1; + } + + } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) { + + int fieldSize = 0; + if (publicKey instanceof iaik.security.ecc.ecdsa.ECPublicKey) { + ECDSAParams params = ((iaik.security.ecc.ecdsa.ECPublicKey) publicKey).getParameter(); + fieldSize = params.getG().getCurve().getField().getSize().bitLength(); + } else if (publicKey instanceof ECPublicKey) { + ECParameterSpec params = ((ECPublicKey) publicKey).getParams(); + fieldSize = params.getCurve().getField().getFieldSize(); + } + + if (useStrongHash && fieldSize >= 512) { + signatureAlgorithm = AlgorithmID.ecdsa_plain_With_SHA512; + digestAlgorithm = AlgorithmID.sha512; + } else if (useStrongHash && fieldSize >= 256) { + signatureAlgorithm = AlgorithmID.ecdsa_plain_With_SHA256; + digestAlgorithm = AlgorithmID.sha256; + } else if (useStrongHash) { + signatureAlgorithm = AlgorithmID.ecdsa_plain_With_RIPEMD160; + digestAlgorithm = AlgorithmID.ripeMd160; + } else { + signatureAlgorithm = AlgorithmID.ecdsa_plain_With_SHA1; + digestAlgorithm = AlgorithmID.sha1; + } + } else { + throw new NoSuchAlgorithmException("Public key algorithm '" + algorithm + + "' not supported."); + } + } + + public byte[] sign(STAL stal, String keyboxIdentifier) throws CMSException { + signedData.setSecurityProvider(new STALSecurityProvider(stal, keyboxIdentifier)); + setSignerInfo(); + ContentInfo contentInfo = new ContentInfo(signedData); + return contentInfo.getEncoded(); + } +} diff --git a/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/testApplicationContext.xml b/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/testApplicationContext.xml index 3ec44394..7d7c967f 100644 --- a/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/testApplicationContext.xml +++ b/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/testApplicationContext.xml @@ -110,6 +110,14 @@ value="http://www.buergerkarte.at/namespaces/securitylayer/1.2#" /> <constructor-arg value="CreateXMLSignatureRequest" /> </bean> + <bean id="createCMSSignatureCommandFactory" + class="at.gv.egiz.bku.slcommands.impl.CreateCMSSignatureCommandFactory" + parent="abstractCommandFactory" /> + <bean id="createCMSSignatureRequest" class="javax.xml.namespace.QName"> + <constructor-arg + value="http://www.buergerkarte.at/namespaces/securitylayer/1.2#" /> + <constructor-arg value="CreateCMSSignatureRequest" /> + </bean> <bean id="getStatusCommandFactory" class="at.gv.egiz.bku.slcommands.impl.GetStatusCommandFactory" parent="abstractCommandFactory" /> <bean id="getStatusRequest" class="javax.xml.namespace.QName"> @@ -126,6 +134,7 @@ <entry key-ref="infoboxReadRequest" value-ref="infoboxReadCommandFactory" /> <entry key-ref="infoboxUpdateRequest" value-ref="infoboxUpdateCommandFactory" /> <entry key-ref="createXMLSignatureRequest" value-ref="createXMLSignatureCommandFactory" /> + <entry key-ref="createCMSSignatureRequest" value-ref="createCMSSignatureCommandFactory" /> <entry key-ref="getStatusRequest" value-ref="getStatusCommandFactory" /> </map> </property> |