From 624dc3a0b6ef39948b9e78841ef7f75f27fee8da Mon Sep 17 00:00:00 2001 From: tkellner Date: Thu, 28 Nov 2013 13:23:09 +0000 Subject: Implement CreateCMSSignatureRequest git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1234 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../bku/slcommands/impl/cms/STALPrivateKey.java | 34 +++ .../slcommands/impl/cms/STALSecurityProvider.java | 76 ++++++ .../gv/egiz/bku/slcommands/impl/cms/Signature.java | 255 +++++++++++++++++++++ 3 files changed, 365 insertions(+) create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALPrivateKey.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/STALSecurityProvider.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms/Signature.java (limited to 'bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/cms') 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 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 CreateCMSSignatureRequest. + * + * @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 attributes = new ArrayList(); + 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 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 attributes) { + Attribute contentType = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.cms_data}); + attributes.add(contentType); + } + + private void setSigningCertificateAttrib(List 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 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(); + } +} -- cgit v1.2.3