-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test to exemplify usage of a custom JCA provider that delegates t…
…he signature operation to an external system
- Loading branch information
1 parent
216d3ec
commit 8683296
Showing
4 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
85 changes: 85 additions & 0 deletions
85
src/test/java/xades4j/production/CustomSignatureProviderSignerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* 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 org.apache.xml.security.utils.Constants; | ||
import org.junit.jupiter.api.AfterAll; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Test; | ||
import org.w3c.dom.Document; | ||
import org.w3c.dom.Element; | ||
import xades4j.algorithms.EnvelopedSignatureTransform; | ||
import xades4j.properties.DataObjectDesc; | ||
import xades4j.providers.CertificateValidationProvider; | ||
import xades4j.providers.KeyingDataProvider; | ||
import xades4j.verification.SignatureSpecificVerificationOptions; | ||
import xades4j.verification.XAdESForm; | ||
import xades4j.verification.XAdESVerificationResult; | ||
import xades4j.verification.XadesVerificationProfile; | ||
import xades4j.verification.XadesVerifier; | ||
|
||
import java.security.Security; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
/** | ||
* Exemplifies usage of a custom JCA provider that delegates the signature operation to an external system. | ||
* | ||
* @see <a href="https://github.com/luisgoncalves/xades4j/issues/287">https://github.com/luisgoncalves/xades4j/issues/287</a> for the reasoning | ||
*/ | ||
public class CustomSignatureProviderSignerTest extends SignerTestBase | ||
{ | ||
static final ExternalSignatureSystemProvider signatureProvider = new ExternalSignatureSystemProvider(); | ||
|
||
@BeforeAll | ||
static void addProvider() | ||
{ | ||
assert Security.addProvider(signatureProvider) != -1; | ||
} | ||
|
||
@AfterAll | ||
static void removeProvider() | ||
{ | ||
Security.removeProvider(signatureProvider.getName()); | ||
} | ||
|
||
@Test | ||
void run() throws Exception | ||
{ | ||
Document doc = getTestDocument(); | ||
Element root = doc.getDocumentElement(); | ||
|
||
// Sign | ||
|
||
KeyingDataProvider keyingDataProvider = signatureProvider.getKeyingDataProvider(); | ||
XadesSigner signer = new XadesBesSigningProfile(keyingDataProvider).newSigner(); | ||
|
||
DataObjectDesc obj1 = new DataObjectReference('#' + root.getAttribute("Id")).withTransform(new EnvelopedSignatureTransform()); | ||
SignedDataObjects dataObjs = new SignedDataObjects(obj1); | ||
signer.sign(dataObjs, root); | ||
|
||
// Verify | ||
|
||
CertificateValidationProvider certificateValidationProvider = ExternalSignatureSystem.getCertificateValidationProvider(); | ||
XadesVerifier verifier = new XadesVerificationProfile(certificateValidationProvider).newVerifier(); | ||
|
||
Element signature = (Element) root.getElementsByTagNameNS(Constants.SignatureSpecNS, Constants._TAG_SIGNATURE).item(0); | ||
XAdESVerificationResult result = verifier.verify(signature, new SignatureSpecificVerificationOptions()); | ||
assertEquals(XAdESForm.BES, result.getSignatureForm()); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/test/java/xades4j/production/ExternalSignatureSystem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* 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.providers.CertificateValidationProvider; | ||
import xades4j.verification.VerifierTestBase; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.security.PrivateKey; | ||
import java.security.Signature; | ||
import java.security.SignatureException; | ||
import java.security.cert.X509Certificate; | ||
|
||
/** | ||
* A fictitious external system that performs signature operations. | ||
* <p> | ||
* The {@link #sign(ByteBuffer, String)} method is the entrypoint and could be implemented in any way. | ||
* The example implementation uses the existing test key material and JCA. | ||
*/ | ||
final class ExternalSignatureSystem | ||
{ | ||
private static final X509Certificate _certificate; | ||
private static final PrivateKey _key; | ||
|
||
static | ||
{ | ||
try | ||
{ | ||
_certificate = SignerTestBase.keyingProviderMy.getSigningCertificateChain().get(0); | ||
_key = SignerTestBase.keyingProviderMy.getSigningKey(_certificate); | ||
} | ||
catch (Exception e) | ||
{ | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public static X509Certificate getSigningCertificate() throws Exception | ||
{ | ||
return _certificate; | ||
|
||
} | ||
|
||
public static CertificateValidationProvider getCertificateValidationProvider() | ||
{ | ||
return VerifierTestBase.validationProviderMySigs; | ||
} | ||
|
||
public static byte[] sign(ByteBuffer content, String algorithm) throws SignatureException | ||
{ | ||
try | ||
{ | ||
Signature s = Signature.getInstance(algorithm); | ||
s.initSign(_key); | ||
s.update(content); | ||
return s.sign(); | ||
} | ||
catch (Exception e) | ||
{ | ||
throw new SignatureException(e); | ||
} | ||
} | ||
} |
164 changes: 164 additions & 0 deletions
164
src/test/java/xades4j/production/ExternalSignatureSystemProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/* | ||
* 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.providers.KeyingDataProvider; | ||
import xades4j.providers.impl.DirectKeyingDataProvider; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.security.InvalidKeyException; | ||
import java.security.InvalidParameterException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.PrivateKey; | ||
import java.security.Provider; | ||
import java.security.PublicKey; | ||
import java.security.SignatureException; | ||
import java.security.SignatureSpi; | ||
import java.util.Set; | ||
|
||
/** | ||
* Provider that delegates signature operations to a fictitious {@link ExternalSignatureSystem}. | ||
* | ||
* @see <a href="https://docs.oracle.com/en/java/javase/11/security/howtoimplaprovider.html">How to Implement a Provider</a> | ||
*/ | ||
final class ExternalSignatureSystemProvider extends Provider | ||
{ | ||
private static final Set<String> _supportedSignatureAlgorithms = Set.of( | ||
"SHA256withRSA" | ||
); | ||
|
||
public ExternalSignatureSystemProvider() | ||
{ | ||
super(ExternalSignatureSystemProvider.class.getSimpleName(), "1.0", ""); | ||
_supportedSignatureAlgorithms.forEach(alg -> { | ||
putService(new SignatureService(this, alg)); | ||
}); | ||
} | ||
|
||
public KeyingDataProvider getKeyingDataProvider() throws Exception | ||
{ | ||
return new DirectKeyingDataProvider( | ||
// The signing certificate to be added to the signature is whatever is used by the external system | ||
ExternalSignatureSystem.getSigningCertificate(), | ||
// The signing key is just an handler/marker that this provider can handle. The key instance could have | ||
// some parameters to control the behavior of the external system (e.g. a key ID). | ||
new MarkerPrivateKey()); | ||
} | ||
|
||
private static final class SignatureService extends Provider.Service | ||
{ | ||
public SignatureService(Provider provider, String algorithm) | ||
{ | ||
super(provider, "Signature", algorithm, SignatureAdapter.class.getName(), null, null); | ||
} | ||
|
||
@Override | ||
public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException | ||
{ | ||
return new SignatureAdapter(getAlgorithm()); | ||
} | ||
} | ||
|
||
private static final class MarkerPrivateKey implements PrivateKey | ||
{ | ||
@Override | ||
public String getAlgorithm() | ||
{ | ||
return "RSA"; | ||
} | ||
|
||
@Override | ||
public String getFormat() | ||
{ | ||
return null; | ||
} | ||
|
||
@Override | ||
public byte[] getEncoded() | ||
{ | ||
return null; | ||
} | ||
} | ||
|
||
/** | ||
* The actual implementation of the Signature operation. | ||
*/ | ||
private static final class SignatureAdapter extends SignatureSpi | ||
{ | ||
private final String _algorithm; | ||
private final ByteBuffer _buffer; | ||
|
||
private SignatureAdapter(String algorithm) | ||
{ | ||
_algorithm = algorithm; | ||
_buffer = ByteBuffer.allocate(1024 * 4); | ||
} | ||
|
||
@Override | ||
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException | ||
{ | ||
if (!(privateKey instanceof MarkerPrivateKey)) | ||
{ | ||
throw new InvalidKeyException(); | ||
} | ||
} | ||
|
||
@Override | ||
protected void engineUpdate(byte b) throws SignatureException | ||
{ | ||
_buffer.put(b); | ||
} | ||
|
||
@Override | ||
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException | ||
{ | ||
_buffer.put(b, off, len); | ||
} | ||
|
||
@Override | ||
protected byte[] engineSign() throws SignatureException | ||
{ | ||
_buffer.flip(); // set buffer positions and limit for reading | ||
return ExternalSignatureSystem.sign(_buffer, _algorithm); | ||
} | ||
|
||
@Override | ||
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException | ||
{ | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
protected boolean engineVerify(byte[] sigBytes) throws SignatureException | ||
{ | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
protected void engineSetParameter(String param, Object value) throws InvalidParameterException | ||
{ | ||
throw new InvalidParameterException(); | ||
} | ||
|
||
@Override | ||
protected Object engineGetParameter(String param) throws InvalidParameterException | ||
{ | ||
throw new InvalidParameterException(); | ||
} | ||
} | ||
} |