I'm using JDK 1.8.0_74.
I have code for creating an XML Digital Signature based on a standard way of doing things (e.g. http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html).
However, instead of loading a key store and key for the X509Data element of the signature block, I simply want to load an X509Certificate as part of the signature block, which would yield something like this:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#body">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>d6xssQEvw9mMXEbQ1+/jpYTFbqY=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>HwQ5o5RzCauJSIcyGyzPlIJHMYtaA2spBmnRvTmcL0S+bfb/UovUwBAn7WAKckUH
Qv0TuRMMZG3xaV5h4tdrW3hgSw1wZFfEG9cxViz6cr7FOTOEfOAjtU3M8v2/f21i
4o7w5ZORwAlUONamQ0C9x5CNccvNZln5vrpdcL+vqSc=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<X509Certificate xmlns="http://www.w3.org/2000/09/xmldsig#">MIIC9TCCAl6gAwIBAgICFNAwDQYJKoZIhvcNAQEEBQAwgYcxCzAJBgNVBAYTAlpB
MSIwIAYDVQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRU
aGF3dGUgQ2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAa
BgNVBAMTE1RoYXd0ZSBUZXN0IENBIFJvb3QwHhcNMDUwNTI2MTkxNzE2WhcNMDUw
NjE2MTkxNzE2WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNBTElGT1JOSUEx
EjAQBgNVBAcTCVNBTiBQRURSTzEPMA0GA1UEChMGQURWRU5UMR8wHQYDVQQLFBZS
RVNFQVJDSCAmIERFVkVMT1BNRU5UMScwJQYDVQQDEx5hZHZjZW50cmFsLmFkdmVu
dHJlc291cmNlcy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANQQh/K9
GBezwbZuES/zX1exs+9/RDGH2Gv5b5mzSsytg4AY/gYN65tbL5PT/ZsfMs0QYpl0
KGxJFnZ5HHlR/90Ut86PhQPsVsr3S0iWJCrPHaPE8yWt4MKWP/5ERIf1C11cvRvh
bYmrIab5+bxOhYzci3t7aRBf2Er6m89Fd5SjAgMBAAGjZDBiMAwGA1UdEwEB/wQC
MAAwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL3d3dy50aGF3dGUuY29tL3Rlc3Rj
ZXJ0LmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcN
AQEEBQADgYEAJz3h8DT7kTvCPVhxOpRV1pE31QQaf4kD5/qI0D9yRYck2RkLH+ow
U1OQODhRyAkJUhbjfkWJPnjJUoYTmtlJioWq0r6lYJPv9QKgg4LwMo/RGvhdfHMc
83/Q5HSKWdat3US79R8K6FALTq3Ij7m4nJZZ6Zi+Ev8JBsSGsedSt+o=</X509Certificate>
<X509IssuerSerial xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509IssuerName>CN=Thawte Test CA Root,OU=TEST TEST TEST,O=Thawte Certification,ST=FOR TESTING PURPOSES ONLY,C=ZA</X509IssuerName>
<X509SerialNumber>5328</X509SerialNumber>
</X509IssuerSerial>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
(BTW, this is a valid signature that I've verified previously.)
Now, I have code that looks like this:
javax.xml.crypto.dsig.keyinfo.KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
X509Certificate certObj1 = null;
try {
InputStream fis = this.getClass().getClassLoader().getResourceAsStream("test.cert");
certObj1 = (X509Certificate)(X509Certificate.getInstance(fis));
fis.close();
fis = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (javax.security.cert.CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x509Content.add(certObj1);
x509Content.add(certObj1.getSubjectDN().getName());
javax.xml.crypto.dsig.keyinfo.X509Data xd = kif.newX509Data(x509Content);
javax.xml.crypto.dsig.keyinfo.KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
But, when this line is executed:
javax.xml.crypto.dsig.keyinfo.X509Data xd = kif.newX509Data(x509Content);
I get an exception:
java.lang.ClassCastException: content[0] is not a valid X509Data type
at org.jcp.xml.dsig.internal.dom.DOMX509Data.<init>(DOMX509Data.java:90)
at org.jcp.xml.dsig.internal.dom.DOMKeyInfoFactory.newX509Data(DOMKeyInfoFactory.java:106)
at com.abc.service.TestService.generateSignature(TestService.java:33...
Having looked through the appropriate code (both online and in the actual .class file decompiled), it fails in this section of the DOMX509Data constructor:
if (x509Type instanceof String) {
new X500Principal((String)x509Type);
} else if (!(x509Type instanceof byte[]) &&
!(x509Type instanceof X509Certificate) &&
!(x509Type instanceof X509CRL) &&
!(x509Type instanceof XMLStructure)) {
throw new ClassCastException
("content["+i+"] is not a valid X509Data type");
}
But, if I copy/paste and recreate that same code in my own code, that exception NEVER happens!
For the life of me, I can't figure out why this might be. I've checked that the type of content[0]
is com.sun.security.cert.internal.x509.X509V1CertImpl
which most definitely extends X509Certificate
.
What am I doing wrong? I'm hoping I've overlooked something or I'm doing something wholly illegal.
Any help would be greatly appreciated.
Thanks in advance.
UPDATE
While I'd still like to know why this is, in the interest time of time, I've followed the example here (http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html) and it loads in the certificate body itself, but you first have to convert the cert into a keystore (this might help: importing an existing x509 certificate and private key in Java keystore to use in ssl).
Also, if you just want to load the serial info instead of the certificate body itself, try something like this:
String dn = cert.getIssuerDN().toString();
BigInteger sn = cert.getSerialNumber();
X509IssuerSerial xd = kif.newX509IssuerSerial(dn, sn);
// next commented line was the original, replaced with the above line
//javax.xml.crypto.dsig.keyinfo.X509Data xd = kif.newX509Data(x509Content);
javax.xml.crypto.dsig.keyinfo.KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kif.newX509Data(Collections.singletonList(xd))));