Reputation: 2183
I am using Xades4J to develop a simple Java program that signs and verifies an XML with XADES-EPES. The sign process seems to be working properly and generates a valid signed XML (although we don't need the SignaturePolicyIdentifier section on it).
*options arguments is from joptsimple library to parse arguments
This is my sign function:
void signXML(OptionSet options) {
KeyingDataProvider kp;
try {
SignaturePolicyInfoProvider policyInfoProvider = new SignaturePolicyInfoProvider() {
public SignaturePolicyBase getSignaturePolicy() {
return new SignaturePolicyIdentifierProperty(
new ObjectIdentifier("oid:/1.2.4.0.9.4.5", IdentifierType.OIDAsURI, "Policy description"),
new ByteArrayInputStream("Test policy input stream".getBytes()))
.withLocationUrl("http://www.example.com/policy");
}
};
kp = new FileSystemKeyStoreKeyingDataProvider("pkcs12", options.valueOf("certificate").toString(),
new FirstCertificateSelector(), new DirectPasswordProvider(options.valueOf("password").toString()),
new DirectPasswordProvider(options.valueOf("password").toString()), false);
// SignaturePolicyInfoProvider spi = new
XadesSigningProfile p = new XadesEpesSigningProfile(kp, policyInfoProvider);
p.withBasicSignatureOptionsProvider(new SignatureOptionsProvider());
XadesSigner signer = p.newSigner();
// open file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = null;
builder = factory.newDocumentBuilder();
Document doc1 = builder.parse(new File(options.valueOf("input").toString()));
Element elemToSign = doc1.getDocumentElement();
// sign whole document
new Enveloped(signer).sign(elemToSign);
// save output file
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(new File(options.valueOf("output").toString()));
Source input = new DOMSource(doc1);
transformer.transform(input, output);
} catch (KeyStoreException | ParserConfigurationException | SAXException | IOException | XAdES4jException
| TransformerException e) {
e.printStackTrace();
}
}
This is a sample signature that it creates: (Is it normal to have at the end of each line of the base64 values?)
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686-ref0" URI="">
<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>h20v8PSCwU5ymBKMj5o8scN3FyMfGCmN8OA2bnRHTnI=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-f5700b65-334e-4905-96f6-ca6156139686-signedprops">
<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>tEyMfk39qnevJRyLbxQJIGpEBJuCcYRSZNNjm7HfaaA=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686-sigvalue">
ZhdQPEz7KwYoLTc72rxqUN2rlMfRGmqHd0MVSEeHRf/kmoqAGAqit5tw6k9k8oFCyuPDS/+DoXHJ
rooMDEMr+cuvbwwDwy77kHT+8JeUuIAamyqP/OByLytxXm/oKHVAraMhHNsGHvhcsYzULHl+n6JB
fhsYeR6rZdksS+QkgeUYohEktodl20/kyug4ymZjbJ7rCtH6+nRRR1nqc8oVT2ZpgfEaPHqWO06V
QURYYkuQEPOsNgFUu+YiWAX9pz94tlsOlLu6efBUP4lDZbCSq75J7nVZonmTbj/+mPl/oxpLCAgG
utWpL1RwyWn454G+Rd/AG3+MLWnbPVVSWUTcNK2gv4S2MVmosGxBnKwA71Zu30S+KaDwoktzZ62X
ViDLMmf6YbTKhiCIZYdjXPXr9QwSOHr+2B5GNUwo/CoOENJY/sXNHiHXxw+d+cyoowN4faJoVb5C
YFT7DACHyRd/d6TGdVEviHToTak+xPIK0HGnLvmkqzpICX8SFuMybur5Tttex4hqJNmXI7XbI9pk
YGLESBvKmzp4O9wLTJdABsLLxZSwU1plagu+5nb8ujIzmngVEHx4yBRprRVr3cBrHKoQBOHAiaGQ
96qUVTFogIX3vxN/1Q8xKDfB99FlzDdqnc2SW5lgYHnkuHHStJhoaCKRraUWVtU1miT/gMES+YM=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIFgTCCA2mgAwIBAgIJAPzDOBvbJQYvMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAkFVMRMw
EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEDAO
BgNVBAMMB1Fwcm90ZXgwHhcNMTcxMTI5MDIwMjIyWhcNMTgxMTI5MDIwMjIyWjBXMQswCQYDVQQG
EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg
THRkMRAwDgYDVQQDDAdRcHJvdGV4MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt1+k
sAGGc1kvhs3AjTfIyBRRBb3sB5C+IlGplS1lOpViUsYOZKT/t0u1PWTxWnTNYpEChSg4ECnbaNje
nuyIQct5AS+MMMD2R8SSGGfvlhwr/yypEzcIfsOjFMhnV95Kto0abqLDucOyZotZfbaQQcwK7ATI
/TrumoUX6lRaryeIYBhENRCvmTjcR3ecinL+FSEpEOdXSh0jtHRtoV/HCzHdgNvXXLPD2YkyjY/0
D9rvHmVmKXtRfnLsiNcc2vMuE0XlfBz/WbP+lOpMKpka5F7ocnDeMJ2Tb24tL60M+DS7IYWOJ4wm
Vpb9FsBI3mDZ1AndsTIp5lljLIT7CVvgIvIbCxlOx6vcRxjOhpzNLmCJuMuI2dLtg5qn6q4QoLrE
6LAQZ8Zt9ljK2s33Vc84FZi+nZx6A1enTnLyHjds6BdrzvKLOJeUtLlTbufxNrkQzWSIhI2ZbfhY
Mn4yC1REj4kH30AKD6YkN028TasJDnv30g9n9eSbZ0fq2buT2DawGAA9GP02daaaOyTmBVJMonpi
xSD7lN7iR3zvDUxtjIoOi15XZw+2k28VqkwVdOwo3xkz5zVhSxm7Xv1NHdiUO2AUzvWMdBNMTS2t
0rr31B232UQvuqDHW1ws6em1iA+Lu1X3Z59Mer6Hg2mVclNKSBrYxUSwSv9poZqxXlGH6R0CAwEA
AaNQME4wHQYDVR0OBBYEFNG3ss8QEfGFgyxHpb54iRRNz9BDMB8GA1UdIwQYMBaAFNG3ss8QEfGF
gyxHpb54iRRNz9BDMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBACuhTI0prkRc15OI
HGQq+xhHRfbnG3dUEaXuAYhyDbrRXz2urEjIf2vGPIn7hheOrScSbQgblHq/tV1nQTyRsyHCvZlV
aE2+OWCfrQDdrwZKE2N2xC1ALgGTtF+I4lXgy6/9OzNVFnV0oZUnO6PN5cH6SVy+RHpO9Lsr5ndI
YBSuu0Zd7/IsOYY/vWcfJjq3+SnRotgGj0Jf4F7SH74XPz0QkoVKM1z3zGlbF/w8OzJw71yRVkaX
Ld+TDFC6jDFV+kjQ1k8pa4Hjs2Yko2AmNmpOZmMVjRLTdGLdSU/Gt1ewDp5TsEa2y/5u2PcZY/fd
dNaMyH7Qx0SmqvYvgG0C3mSGhhFv6++SaXXmo1lQCDgY9OK975KnQPSIHXeuK7oh/82a2ZhmQau0
B/ISxrjtRJbSHByLefJAkJpRVGowa14Nq/jI/Dvl46LVepTq2eNxbbMmbaW60HMwDS+YfNIwRpRS
n/W1/7mWLQRRP0Mb1/fjZlIZBV83fynZU5BSsoW+LZ8EWXt+VhtC8SWZ3m3ffk3KKqZDKJTih5c1
IFz2veIrHZH6O9n3taU+mVuNC8piuG+GBCBPo7dQwFmGo2zZ9BvSqUdytLniuej1PcPBTkcxfhCN
h5lKWwZCNE5v8RlEd9RbjEC5nAm8kCVvHX5SZivd77Yum8eBMAUQ++M2oNfz
</ds:X509Certificate>
<ds:X509SubjectName>CN=Qprotex,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU</ds:X509SubjectName>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=Qprotex,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU</ds:X509IssuerName>
<ds:X509SerialNumber>18213463010308326959</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>
t1+ksAGGc1kvhs3AjTfIyBRRBb3sB5C+IlGplS1lOpViUsYOZKT/t0u1PWTxWnTNYpEChSg4ECnb
aNjenuyIQct5AS+MMMD2R8SSGGfvlhwr/yypEzcIfsOjFMhnV95Kto0abqLDucOyZotZfbaQQcwK
7ATI/TrumoUX6lRaryeIYBhENRCvmTjcR3ecinL+FSEpEOdXSh0jtHRtoV/HCzHdgNvXXLPD2Yky
jY/0D9rvHmVmKXtRfnLsiNcc2vMuE0XlfBz/WbP+lOpMKpka5F7ocnDeMJ2Tb24tL60M+DS7IYWO
J4wmVpb9FsBI3mDZ1AndsTIp5lljLIT7CVvgIvIbCxlOx6vcRxjOhpzNLmCJuMuI2dLtg5qn6q4Q
oLrE6LAQZ8Zt9ljK2s33Vc84FZi+nZx6A1enTnLyHjds6BdrzvKLOJeUtLlTbufxNrkQzWSIhI2Z
bfhYMn4yC1REj4kH30AKD6YkN028TasJDnv30g9n9eSbZ0fq2buT2DawGAA9GP02daaaOyTmBVJM
onpixSD7lN7iR3zvDUxtjIoOi15XZw+2k28VqkwVdOwo3xkz5zVhSxm7Xv1NHdiUO2AUzvWMdBNM
TS2t0rr31B232UQvuqDHW1ws6em1iA+Lu1X3Z59Mer6Hg2mVclNKSBrYxUSwSv9poZqxXlGH6R0=
</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</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="#xmldsig-f5700b65-334e-4905-96f6-ca6156139686"><xades:SignedProperties Id="xmldsig-f5700b65-334e-4905-96f6-ca6156139686-signedprops"><xades:SignedSignatureProperties><xades:SigningTime>2017-11-30T20:58:18.009-05:00</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>JkEubWWYvvoh7kOnuAvYeXE4cFamHJ5FOBYt3b3dCj4=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>CN=Qprotex,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU</ds:X509IssuerName><ds:X509SerialNumber>18213463010308326959</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate><xades:SignaturePolicyIdentifier><xades:SignaturePolicyId><xades:SigPolicyId><xades:Identifier Qualifier="OIDAsURI">oid:/1.2.4.0.9.4.5</xades:Identifier><xades:Description>Policy description</xades:Description></xades:SigPolicyId><xades:SigPolicyHash><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>MaW9PDxJruPHhjBVAMWyrF9zP+kMnNqkoQOUXxDDGAk=</ds:DigestValue></xades:SigPolicyHash><xades:SigPolicyQualifiers><xades:SigPolicyQualifier><xades:SPURI>http://www.example.com/policy</xades:SPURI></xades:SigPolicyQualifier></xades:SigPolicyQualifiers></xades:SignaturePolicyId></xades:SignaturePolicyIdentifier></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object>
</ds:Signature>
And this is my verify code that prints Bad XML signature.
void verifyXML(OptionSet options) {
try {
File file = new File(options.valueOf("certificate").toString());
FileSystemDirectoryCertStore store = new FileSystemDirectoryCertStore(file.getAbsoluteFile().getParent());
FileInputStream fis = new FileInputStream(options.valueOf("certificate").toString());
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(fis, options.valueOf("password").toString().toCharArray());
fis.close();
CertificateValidationProvider provider = new PKIXCertificateValidationProvider(ks, false, store.getStore());
XadesVerifier verifier = new XadesVerificationProfile(provider).newVerifier();
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(options.valueOf("input").toString()));
doc.getDocumentElement().normalize();
Element sig = (Element)doc.getElementsByTagName("ds:Signature").item(0);
PrintWriter out = null;
try {
out = new PrintWriter(options.valueOf("output").toString());
XAdESVerificationResult result = verifier.verify(sig, null);
out.println("true");
} catch (Exception e) {
System.out.println(e.getMessage());
out.println("false");
}
out.close();
} catch (Exception ex) {
System.err.println("Error: " + ex.getMessage());
}
I will appreciate if someone can point me where the issue could be. Thanks!
UPDATE After I checked the stack trace I found the following exception:
xades4j.xml.unmarshalling.UnmarshalException: Bad XML signature
at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:130)
at com.qprotex.Main.verifyXML(Main.java:190)
at com.qprotex.Main.<init>(Main.java:99)
at com.qprotex.Main.main(Main.java:81)
Caused by: org.apache.xml.security.exceptions.XMLSecurityException: Cannot create a null:null from a http://www.w3.org/2000/09/xmldsig#:Signature element
I found this post that mentions the same error and after enabling factory.setNamespaceAware(true) in the verification method I get the following exception:
java.lang.NullPointerException
at xades4j.xml.unmarshalling.FromXmlSignaturePolicyConverter.getLocationUrl(FromXmlSignaturePolicyConverter.java:71)
at xades4j.xml.unmarshalling.FromXmlSignaturePolicyConverter.convertFromObjectTree(FromXmlSignaturePolicyConverter.java:64)
at xades4j.xml.unmarshalling.FromXmlSignaturePolicyConverter.convertFromObjectTree(FromXmlSignaturePolicyConverter.java:1)
at xades4j.xml.unmarshalling.UnmarshallerModule.convertProperties(UnmarshallerModule.java:64)
at xades4j.xml.unmarshalling.DefaultQualifyingPropertiesUnmarshaller.unmarshalProperties(DefaultQualifyingPropertiesUnmarshaller.java:83)
at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:175)
at com.qprotex.Main.verifyXML(Main.java:190)
at com.qprotex.Main.<init>(Main.java:99)
at com.qprotex.Main.main(Main.java:81)
UPDATE 2 As I am not using a a SignaturePolicyIdentifier, I did changed my sign function to use XadesBesSigningProfile instead of XadesEpesSigningProfile. The only issue I have now is that it works only if I use KeyStore.getInstance("Windows-ROOT")
This works:
KeyStore trustAnchors = KeyStore.getInstance("Windows-ROOT");
trustAnchors.load(null);
CertificateValidationProvider provider = new PKIXCertificateValidationProvider(trustAnchors, false);
But this does not work and I get back a "xades4j.providers.CannotBuildCertificationPathException: Trust anchors KeyStore has no trusted certificate entries" exception.
FileInputStream fis = new FileInputStream(options.valueOf("certificate").toString());
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(fis, options.valueOf("password").toString().toCharArray());
fis.close();
CertificateValidationProvider provider = new PKIXCertificateValidationProvider(ks, false);
Upvotes: 1
Views: 3239
Reputation: 2090
SignaturePolicyIdentifier
, why are you using XAdES-EPES? You could use XadesBesSigningProfile
instead and produce a XAdES-BES signature.withPolicyDocumentProvider
on the XadesVerificationProfile
.After UPDATE 2:
The exception message seems clear. Your goal is to use the signing certificate as the trust anchor as well? The type of the entry on the keystore needs to be "trust anchor". I guess you should add 2 entries: one with cert/key (type "certificate") and another only with the cert (type "trust anchor"). Or use separate keystores.
Upvotes: 2