Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for signature algorithm parameters #313

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions src/main/java/xades4j/production/SignatureAlgorithms.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import xades4j.algorithms.CanonicalXMLWithoutComments;
import xades4j.algorithms.GenericAlgorithm;

import java.security.spec.AlgorithmParameterSpec;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -40,7 +41,7 @@
*/
public final class SignatureAlgorithms
{
private final Map<String, Algorithm> keyAlgToSignatureAlg = new HashMap<>();
private final Map<String, SignatureMethodAlgorithm> keyAlgToSignatureAlg = new HashMap<>();
private Algorithm canonicalizationAlgorithmForSignature = new CanonicalXMLWithoutComments();
private Algorithm canonicalizationAlgorithmForTimeStampProperties = new CanonicalXMLWithoutComments();
private String digestAlgorithmForDataObjectReferences = MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256;
Expand All @@ -49,9 +50,9 @@ public final class SignatureAlgorithms

public SignatureAlgorithms()
{
keyAlgToSignatureAlg.put("DSA", new GenericAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_DSA));
keyAlgToSignatureAlg.put("RSA", new GenericAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256));
keyAlgToSignatureAlg.put("EC", new GenericAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA256));
keyAlgToSignatureAlg.put("DSA", new SignatureMethodAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_DSA));
keyAlgToSignatureAlg.put("RSA", new SignatureMethodAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256));
keyAlgToSignatureAlg.put("EC", new SignatureMethodAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA256));
}

/**
Expand All @@ -63,11 +64,27 @@ public SignatureAlgorithms()
*/
public SignatureAlgorithms withSignatureAlgorithm(String keyAlgorithmName, String signatureAlgorithm)
{
keyAlgToSignatureAlg.put(keyAlgorithmName, new GenericAlgorithm(signatureAlgorithm));
keyAlgToSignatureAlg.put(keyAlgorithmName, new SignatureMethodAlgorithm(signatureAlgorithm));
return this;
}

Algorithm getSignatureAlgorithm(String keyAlgorithmName) throws UnsupportedAlgorithmException
/**
* Set the signature algorithm and parameters to be used when the signing key has the given key algorithm.
*
* @param keyAlgorithmName the key's algorithm name as defined in JCA standard algorithm names
* @param signatureAlgorithm the signature algorithm
* @param parameters the signature algorithm parameters
* @return the current instance
*/
public SignatureAlgorithms withSignatureAlgorithm(String keyAlgorithmName,
String signatureAlgorithm,
AlgorithmParameterSpec parameters)
{
keyAlgToSignatureAlg.put(keyAlgorithmName, new SignatureMethodAlgorithm(signatureAlgorithm, parameters));
return this;
}

SignatureMethodAlgorithm getSignatureAlgorithm(String keyAlgorithmName) throws UnsupportedAlgorithmException
{
var algorithm = keyAlgToSignatureAlg.get(keyAlgorithmName);

Expand Down
45 changes: 45 additions & 0 deletions src/main/java/xades4j/production/SignatureMethodAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* XAdES4j - A Java library for generation and verification of XAdES signatures.
* Copyright (C) 2024 Luis Goncalves.
*
* XAdES4j 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 3 of the License, or any later version.
*
* XAdES4j 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 XAdES4j. If not, see <http://www.gnu.org/licenses/>.
*/

package xades4j.production;

import xades4j.algorithms.Algorithm;

import javax.annotation.Nullable;
import java.security.spec.AlgorithmParameterSpec;

final class SignatureMethodAlgorithm extends Algorithm
{
private final AlgorithmParameterSpec parameters;

SignatureMethodAlgorithm(String uri, @Nullable AlgorithmParameterSpec parameters)
{
super(uri);
this.parameters = parameters;
}

SignatureMethodAlgorithm(String uri)
{
this(uri, null);
}

@Nullable
AlgorithmParameterSpec getParameters()
{
return parameters;
}
}
42 changes: 20 additions & 22 deletions src/main/java/xades4j/production/SignerBES.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.ElementProxy;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Expand Down Expand Up @@ -316,41 +315,40 @@ private String getDigestAlgUri()

private XMLSignature createSignature(Document signatureDocument, String baseUri, String signingKeyAlgorithm) throws XAdES4jXMLSigException, UnsupportedAlgorithmException
{
Algorithm signatureAlg = this.signatureAlgorithms.getSignatureAlgorithm(signingKeyAlgorithm);
Element signatureAlgElem = createElementForAlgorithm(signatureAlg, Constants._TAG_SIGNATUREMETHOD, signatureDocument);

SignatureMethodAlgorithm signatureAlg = this.signatureAlgorithms.getSignatureAlgorithm(signingKeyAlgorithm);
Algorithm canonAlg = this.signatureAlgorithms.getCanonicalizationAlgorithmForSignature();
if (null == canonAlg)
{
throw new NullPointerException("Canonicalization algorithm not provided");
}
Element canonAlgElem = createElementForAlgorithm(canonAlg, Constants._TAG_CANONICALIZATIONMETHOD, signatureDocument);

XMLSignature signature;
try
{
return new XMLSignature(signatureDocument, baseUri, signatureAlgElem, canonAlgElem);
signature = new XMLSignature(
signatureDocument,
baseUri,
signatureAlg.getUri(),
0,
canonAlg.getUri(),
null,
signatureAlg.getParameters());
}
catch (XMLSecurityException ex)
{
// Following the code, doesn't seem to be thrown at all.
throw new XAdES4jXMLSigException(ex.getMessage(), ex);
}
}

private Element createElementForAlgorithm(Algorithm algorithm, String elementName, Document signatureDocument) throws UnsupportedAlgorithmException
{
Element algorithmElem = XMLUtils.createElementInSignatureSpace(signatureDocument, elementName);
algorithmElem.setAttributeNS(null, Constants._ATT_ALGORITHM, algorithm.getUri());

List<Node> algorithmParams = this.algorithmsParametersMarshaller.marshalParameters(algorithm, signatureDocument);
if (algorithmParams != null)
List<Node> canonAlgParams = this.algorithmsParametersMarshaller.marshalParameters(canonAlg, signatureDocument);
if (canonAlgParams != null)
{
for (Node p : algorithmParams)
Element canonAlgElement = DOMHelper.getChildElementsByTagNameNS(
signature.getSignedInfo().getElement(),
Constants.SignatureSpecNS, Constants._TAG_CANONICALIZATIONMETHOD
).get(0);
for (Node p : canonAlgParams)
{
algorithmElem.appendChild(p);
canonAlgElement.appendChild(p);
}
}
return algorithmElem;

return signature;
}

private static void digestManifests(Iterable<Manifest> manifests) throws XAdES4jXMLSigException
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/xades4j/utils/DOMHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
*/
package xades4j.utils;

import java.util.ArrayList;
import java.util.Collection;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.HelperNodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.ArrayList;
import java.util.List;

/**
* Utility methods for DOM nodes.
* @author Luís
Expand Down Expand Up @@ -163,10 +164,10 @@ public static Element getFirstDescendant(
* @param localName element name
* @return
*/
public static Collection<Element> getChildElementsByTagNameNS(Element e, String namespaceURI, String localName)
public static List<Element> getChildElementsByTagNameNS(Element e, String namespaceURI, String localName)
{
Node node = e.getFirstChild();
Collection<Element> elements = new ArrayList<>();
List<Element> elements = new ArrayList<>();

while (node != null)
{
Expand Down
6 changes: 5 additions & 1 deletion src/test/java/xades4j/production/OtherSignerTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ void testSignatureAlgorithms() throws Exception
XadesSigner signer = new XadesCSigningProfile(keyingProviderNist, vdp)
.withSignatureAlgorithms(new SignatureAlgorithms()
.withSignatureAlgorithm("RSA", ALGO_ID_SIGNATURE_RSA_SHA512)
.withCanonicalizationAlgorithmForTimeStampProperties(new ExclusiveCanonicalXMLWithoutComments())
.withCanonicalizationAlgorithmForTimeStampProperties(new ExclusiveCanonicalXMLWithoutComments("x", "y"))
.withDigestAlgorithmForReferenceProperties(ALGO_ID_DIGEST_SHA512))
.with(DEFAULT_TEST_TSA)
.newSigner();
Expand All @@ -157,6 +157,8 @@ void testSignatureAlgorithms() throws Exception

var tsC14n = (Attr) xpath.evaluate("//xades:UnsignedSignatureProperties/xades:SignatureTimeStamp/ds:CanonicalizationMethod/@Algorithm", doc, XPathConstants.NODE);
assertEquals(ALGO_ID_C14N_EXCL_OMIT_COMMENTS, tsC14n.getValue());
var tsC14nParam = (Attr) xpath.evaluate("//xades:UnsignedSignatureProperties/xades:SignatureTimeStamp/ds:CanonicalizationMethod/exc-c14n:InclusiveNamespaces/@PrefixList", doc, XPathConstants.NODE);
assertEquals("x y", tsC14nParam.getValue());

var certReferenceDigest = (Attr) xpath.evaluate("//xades:UnsignedSignatureProperties/xades:CompleteCertificateRefs/xades:CertRefs/xades:Cert/xades:CertDigest/ds:DigestMethod/@Algorithm", doc, XPathConstants.NODE);
assertEquals(ALGO_ID_DIGEST_SHA512, certReferenceDigest.getValue());
Expand All @@ -175,6 +177,8 @@ public String getNamespaceURI(String prefix)
return QualifyingProperty.XADES_XMLNS;
case "xades141":
return QualifyingProperty.XADESV141_XMLNS;
case "exc-c14n":
return "http://www.w3.org/2001/10/xml-exc-c14n#";
}
return null;
}
Expand Down
18 changes: 18 additions & 0 deletions src/test/java/xades4j/production/SignerBESTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package xades4j.production;

import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.utils.resolver.implementations.ResolverDirectHTTP;
import org.apache.xml.security.utils.resolver.implementations.ResolverLocalFilesystem;
import org.junit.jupiter.api.Test;
Expand All @@ -36,6 +37,8 @@
import xades4j.properties.SigningCertificateProperty;

import java.io.File;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -92,6 +95,21 @@ void testSignBESWithEllipticCurveKey() throws Exception
outputDocument(doc, "document.signed.bes.ec.xml");
}

@Test
void testSignBESWithRsaPss() throws Exception
{
Document doc = getTestDocument();
Element elemToSign = doc.getDocumentElement();

XadesSigner signer = new XadesBesSigningProfile(keyingProviderMy)
.withSignatureAlgorithms(new SignatureAlgorithms()
.withSignatureAlgorithm("RSA", XMLSignature.ALGO_ID_SIGNATURE_RSA_PSS, new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 64, 1)))
.newSigner();
new Enveloped(signer).sign(elemToSign);

outputDocument(doc, "document.signed.bes.rsa_pss.xml");
}

@Test
void testSignBESExternalRes() throws Exception
{
Expand Down
7 changes: 7 additions & 0 deletions src/test/java/xades4j/verification/XadesVerifierImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ void testVerifyBESCounterSigCounterSig() throws Exception
assertEquals(XAdESForm.BES, result.getSignatureForm());
}

@Test
void testVerifyBesSigAlgParams() throws Exception
{
var result = verifySignature("document.signed.bes.rsa_pss.xml");
assertEquals(XAdESForm.BES, result.getSignatureForm());
}

@Test
void testVerifyBESEnrichT() throws Exception
{
Expand Down
77 changes: 77 additions & 0 deletions src/test/xml/document.signed.bes.rsa_pss.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?><collection Id="root">
<album>
<title>Questions, unanswered</title>
<artist>Steve and the flubberblubs</artist>
<year>1989</year>
<t:tracks xmlns:t="http://test.xades4j/tracks">
<t:song length="4:05" tracknumber="1">
<t:title>What do you know?</t:title>
<t:artist>Steve and the flubberblubs</t:artist>
<t:lastplayed>2006-10-17-08:31</t:lastplayed>
</t:song>
<t:song length="3:45" tracknumber="2">
<t:title>Who do you know?</t:title>
<t:artist>Steve and the flubberblubs</t:artist>
<t:lastplayed>2006-10-17-08:35</t:lastplayed>
</t:song>
<t:song length="5:14" tracknumber="3">
<t:title>When do you know?</t:title>
<t:artist>Steve and the flubberblubs</t:artist>
<t:lastplayed>2006-10-17-08:39</t:lastplayed>
</t:song>
<t:song length="4:19" tracknumber="4">
<t:title>Do you know?</t:title>
<t:artist>Steve and the flubberblubs</t:artist>
<t:lastplayed>2006-10-17-08:44</t:lastplayed>
</t:song>
</t:tracks>
</album>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="signature-51bb5edf-039c-4c35-ab4b-4304e73a93ee">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2007/05/xmldsig-more#rsa-pss">
<pss:RSAPSSParams xmlns:pss="http://www.w3.org/2007/05/xmldsig-more#">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<pss:SaltLength>64</pss:SaltLength>
<pss:TrailerField>1</pss:TrailerField>
</pss:RSAPSSParams>
</ds:SignatureMethod>
<ds:Reference URI="#root">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>rD/g8soqKz8EiPUBhEWfcQacS0ta4ULHX3dKMEH6ZoQ=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#signedproperties-211e1eb4-e409-4169-a083-06e2201a6e3f">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>GddpcMqEMdYowP4GlEdTiqQcp9Mym1Mz3o9CFSyCet8=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
UKoTfgDL22+mv7q9NmrtFyP55WBj8WNWTM+CXDrPk9aDxps5xS/wHv1X9k1bZK9O2nw4UAqBz42f&#13;
3h3o1or/JrDdI3w5gsY3+rFdzmUq2z0CC5I1yGkCsl9wepxJjFC9R+ODTd1rxzO0rFS+cCM8sOgr&#13;
7olgkXW4K+fViZ0KarY=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIICbTCCAdqgAwIBAgIQpkK0uals+ItHxBlpJuypOTAJBgUrDgMCHQUAMD8xCzAJBgNVBAYTAlBU&#13;
MQ0wCwYDVQQKEwRJU0VMMQswCQYDVQQLEwJDQzEUMBIGA1UEAxMLSXRlcm1lZGlhdGUwHhcNMTAw&#13;
NjI1MTc1ODQ5WhcNMzkxMjMxMjM1OTU5WjBCMQswCQYDVQQGEwJQVDENMAsGA1UEChMESVNFTDEL&#13;
MAkGA1UECxMCQ0MxFzAVBgNVBAMTDkx1aXMgR29uY2FsdmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GN&#13;
ADCBiQKBgQCpP9acMX69Dbg9ciMLFc5dm1tlpTY9OTNZ/EaCYoGVhh/3+DFgyIbEer6SA24hpREm&#13;
AhNG9+Ca0AurDPPgb3aKWFY9pj1WcOctis0VsR0YvzqP+2IGFqKDCd7bXFvv2tI0dEvpdc0oO6PF&#13;
Q02xvJG0kxQf44XljOCjUBU43jkJawIDAQABo28wbTBrBgNVHQEEZDBigBBdbbL4pDKLT56PpOpA&#13;
/56toTwwOjELMAkGA1UEBhMCUFQxDTALBgNVBAoTBElTRUwxCzAJBgNVBAsTAkNDMQ8wDQYDVQQD&#13;
EwZUZXN0Q0GCEN00x9qe7SuWQvpLK0/oay8wCQYFKw4DAh0FAAOBgQBSma8g9dQjiQo4WUljRRuG&#13;
yMUVRyCqW/9oRz8+0EoLNR/AhrIlGqdNbqQ1BkncgNNdqMAus5VD34v/EhgrkgWN5fZajMpYsmcR&#13;
Ahu4PzJ6hggAlWWMy245JwIYuV0s1Oi39GVTxVNOBIX//AONZlGWO4S2Psb1mqdZ99b/MugsaA==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
<ds:Object><xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Target="#signature-51bb5edf-039c-4c35-ab4b-4304e73a93ee"><xades:SignedProperties Id="signedproperties-211e1eb4-e409-4169-a083-06e2201a6e3f"><xades:SignedSignatureProperties><xades:SigningTime>2024-10-18T23:17:54.123+01:00</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>4btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>cn=Itermediate,ou=CC,o=ISEL,c=PT</ds:X509IssuerName><ds:X509SerialNumber>-119284162484605703133798696662099777223</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>cn=TestCA,ou=CC,o=ISEL,c=PT</ds:X509IssuerName><ds:X509SerialNumber>-46248926895392336918291885380930606289</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>cn=TestCA,ou=CC,o=ISEL,c=PT</ds:X509IssuerName><ds:X509SerialNumber>-99704378678639105802976522062798066869</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object>
</ds:Signature></collection>
Loading