Skip to content

Commit b24341b

Browse files
author
Joze RIHTARSIC
committed
[SANTUARIO-615] Implements Pre/Post processors XMLdSIG API for extensions.
1 parent 5dc7a13 commit b24341b

5 files changed

Lines changed: 444 additions & 0 deletions

File tree

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
exports org.apache.jcp.xml.dsig.internal.dom;
3434
exports org.apache.xml.security;
35+
exports org.apache.xml.security.extension;
3536
exports org.apache.xml.security.algorithms;
3637
exports org.apache.xml.security.algorithms.implementations;
3738
exports org.apache.xml.security.c14n;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.xml.security.extension;
20+
21+
import org.apache.xml.security.signature.XMLSignatureException;
22+
23+
/**
24+
* Thrown by a {@link SignatureProcessor} when it cannot complete its processing
25+
* and the signing operation must be aborted.
26+
*
27+
* <p>Extends {@link XMLSignatureException} so callers that already handle the
28+
* standard library exception hierarchy will catch this automatically.
29+
*/
30+
public class SignatureExtensionException extends XMLSignatureException {
31+
32+
private static final long serialVersionUID = 1L;
33+
34+
private final String detailMessage;
35+
36+
/**
37+
* @param message human-readable description of the failure
38+
*/
39+
public SignatureExtensionException(String message) {
40+
super(message);
41+
this.detailMessage = message;
42+
}
43+
44+
/**
45+
* @param message human-readable description of the failure
46+
* @param cause the underlying exception that triggered this failure
47+
*/
48+
public SignatureExtensionException(String message, Throwable cause) {
49+
super(message);
50+
this.detailMessage = message;
51+
if (cause != null) {
52+
initCause(cause);
53+
}
54+
}
55+
56+
@Override
57+
public String getMessage() {
58+
return detailMessage;
59+
}
60+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.xml.security.extension;
20+
21+
import org.apache.xml.security.signature.XMLSignature;
22+
import org.apache.xml.security.signature.XMLSignatureException;
23+
24+
/**
25+
* Extension point for pluggable pre- and post-signature processing hooks in
26+
* the DOM-based XML Signature implementation.
27+
*
28+
* <p>Instances are registered on an {@link XMLSignature} via
29+
* {@link XMLSignature#addPreProcessor(SignatureProcessor)} or
30+
* {@link XMLSignature#addPostProcessor(SignatureProcessor)}.
31+
*
32+
* <ul>
33+
* <li><b>Pre-processors</b> are invoked before digest values are computed on
34+
* the {@code ds:SignedInfo} references. A pre-processor may therefore add
35+
* XML content (e.g., XAdES {@code QualifyingProperties}) that will be
36+
* covered by the signature digest.</li>
37+
* <li><b>Post-processors</b> are invoked after the {@code ds:SignatureValue}
38+
* element has been populated with the completed signature bytes. A
39+
* post-processor may read the final signature value, for example to
40+
* request a signature-timestamp token for XAdES-T.</li>
41+
* </ul>
42+
*
43+
* <p>If a processor throws {@link XMLSignatureException} the signing operation
44+
* is aborted and the exception is propagated to the caller of
45+
* {@link XMLSignature#sign(java.security.Key)}.
46+
*
47+
*/
48+
public interface SignatureProcessor {
49+
50+
/**
51+
* Called during the {@link XMLSignature#sign(java.security.Key)} lifecycle.
52+
*
53+
* @param signature the signature being created; never {@code null}
54+
* @throws XMLSignatureException if processing fails and signing must be aborted
55+
*/
56+
void processSignature(XMLSignature signature) throws XMLSignatureException;
57+
}

src/main/java/org/apache/xml/security/signature/XMLSignature.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@
2727
import java.security.PublicKey;
2828
import java.security.cert.X509Certificate;
2929
import java.security.spec.AlgorithmParameterSpec;
30+
import java.util.ArrayList;
31+
import java.util.List;
32+
import java.util.Objects;
3033

3134
import javax.crypto.SecretKey;
3235

3336
import org.apache.xml.security.algorithms.SignatureAlgorithm;
3437
import org.apache.xml.security.c14n.Canonicalizer;
3538
import org.apache.xml.security.exceptions.XMLSecurityException;
39+
import org.apache.xml.security.extension.SignatureProcessor;
3640
import org.apache.xml.security.keys.KeyInfo;
3741
import org.apache.xml.security.keys.content.X509Data;
3842
import org.apache.xml.security.transforms.Transforms;
@@ -249,6 +253,10 @@ public final class XMLSignature extends SignatureElementProxy {
249253
private static final int MODE_VERIFY = 1;
250254
private int state = MODE_SIGN;
251255

256+
257+
private final List<SignatureProcessor> preProcessors = new ArrayList<>();
258+
private final List<SignatureProcessor> postProcessors = new ArrayList<>();
259+
252260
/**
253261
* This creates a new <CODE>ds:Signature</CODE> Element and adds an empty
254262
* <CODE>ds:SignedInfo</CODE>.
@@ -631,6 +639,29 @@ public XMLSignature(Element element, String baseURI, boolean secureValidation, P
631639
this.state = MODE_VERIFY;
632640
}
633641

642+
643+
/**
644+
* Registers a pre-processor that is invoked before digest values are computed.
645+
* Pre-processors run in registration order.
646+
*
647+
* @param processor the pre-processor to register; must not be {@code null}
648+
*/
649+
public void addPreProcessor(SignatureProcessor processor) {
650+
Objects.requireNonNull(processor, "processor");
651+
preProcessors.add(processor);
652+
}
653+
654+
/**
655+
* Registers a post-processor that is invoked after the {@code ds:SignatureValue}
656+
* element has been populated. Post-processors run in registration order.
657+
*
658+
* @param processor the post-processor to register; must not be {@code null}
659+
*/
660+
public void addPostProcessor(SignatureProcessor processor) {
661+
Objects.requireNonNull(processor, "processor");
662+
postProcessors.add(processor);
663+
}
664+
634665
/**
635666
* Sets the <code>Id</code> attribute
636667
*
@@ -690,6 +721,32 @@ private void setSignatureValueElement(byte[] bytes) {
690721
signatureValueElement.appendChild(t);
691722
}
692723

724+
/**
725+
* Sets an {@code Id} attribute on the {@code ds:SignatureValue} element so
726+
* it can be referenced from unsigned signature properties (e.g., XAdES-T).
727+
*
728+
* @param id the identifier value; {@code null} removes an existing attribute
729+
*/
730+
public void setSignatureValueId(String id) {
731+
if (id != null) {
732+
signatureValueElement.setAttributeNS(null, Constants._ATT_ID, id);
733+
signatureValueElement.setIdAttributeNS(null, Constants._ATT_ID, true);
734+
} else {
735+
signatureValueElement.removeAttributeNS(null, Constants._ATT_ID);
736+
}
737+
}
738+
739+
/**
740+
* Returns the {@code Id} attribute value of the {@code ds:SignatureValue}
741+
* element, or {@code null} if none has been set.
742+
*
743+
* @return the identifier, or {@code null}
744+
*/
745+
public String getSignatureValueId() {
746+
String id = signatureValueElement.getAttributeNS(null, Constants._ATT_ID);
747+
return id.isEmpty() ? null : id;
748+
}
749+
693750
/**
694751
* Returns the KeyInfo child. If we are in signing mode and the KeyInfo
695752
* does not exist yet, it is created on demand and added to the Signature.
@@ -794,6 +851,17 @@ public void sign(Key signingKey) throws XMLSignatureException {
794851
);
795852
}
796853

854+
855+
// snapshot the lists so that concurrent registration during sign() cannot
856+
// cause ConcurrentModificationException or skip newly added processors
857+
List<SignatureProcessor> preSnapshot = List.copyOf(preProcessors);
858+
List<SignatureProcessor> postSnapshot = List.copyOf(postProcessors);
859+
860+
// invoke pre-processors before digests are computed
861+
for (SignatureProcessor processor : preSnapshot) {
862+
processor.processSignature(this);
863+
}
864+
797865
//Create a SignatureAlgorithm object
798866
SignedInfo si = this.getSignedInfo();
799867
SignatureAlgorithm sa = si.getSignatureAlgorithm();
@@ -816,6 +884,11 @@ public void sign(Key signingKey) throws XMLSignatureException {
816884
} catch (XMLSecurityException | IOException ex) {
817885
throw new XMLSignatureException(ex);
818886
}
887+
888+
// invoke post-processors after the signature value has been set
889+
for (SignatureProcessor processor : postSnapshot) {
890+
processor.processSignature(this);
891+
}
819892
}
820893

821894
/**

0 commit comments

Comments
 (0)