Signature as parameter in the authentication request - java

I should get a GET authentication request with a certain set of parameters:
"https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO?" +
"SAMLRequest=" + SAMLRequest + "&SigAlg=" + SigAlg + "&Signature=" + Signature
Thus, I'm interested in the parameters:
SAMLRequest
SigAlg
Signature
When I used OIOSAML, all these parts of the request formed automatically, but it was not possible to flexibly manage them and get full control.
So I decided to write my sP- based Web SSO by using OpenSAML 2.4.1
Consider how to create SAMLRequest. My idP requires that sP- AuthnRequest requests come in this format:
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL=
"https://ip_of_my_sp_here/oiosaml/saml/SAMLAssertionConsumer"
Destination="https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO"
ForceAuthn="false"
ID="_054240e4-...-e0b5e84d3a35"
IsPassive="false"
IssueInstant="2012-02-28T06:43:35.704Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
test_issuer
</saml2:Issuer>
Therefore SAMLRequest I form as follows:
private AuthnRequest buildAuthnRequestObject() {
DateTime issueInstant = new DateTime();
IssuerBuilder issuerBuilder = new IssuerBuilder();
Issuer issuer = issuerBuilder.buildObject(
"urn:oasis:names:tc:SAML:2.0:assertion",
"Issuer", "samlp");
issuer.setValue(issuerId);
AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
AuthnRequest authRequest =
authRequestBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol",
"AuthnRequest", "samlp");
authRequest.setForceAuthn(new Boolean(false));
authRequest.setIsPassive(new Boolean(false));
authRequest.setIssueInstant(issueInstant.plusHours(4));
authRequest.setProtocolBinding(
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
authRequest.setAssertionConsumerServiceURL(consumerUrl);
authRequest.setDestination(idPDestinationUrlSSO);
authRequest.setIssuer(issuer);
authRequest.setID(authReqRandomId);
authRequest.setVersion(SAMLVersion.VERSION_20);
return authRequest;
}
Then this message is compressed and encoded in Base64:
private String encodeRequestMessage(RequestAbstractType requestMessage)
throws MarshallingException, IOException {
Marshaller marshaller =
Configuration.getMarshallerFactory().getMarshaller(requestMessage);
Element authDOM = marshaller.marshall(requestMessage);
Deflater deflater = new Deflater(Deflater.DEFLATED, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream deflaterOutputStream =
new DeflaterOutputStream(byteArrayOutputStream,
deflater);
StringWriter rspWrt = new StringWriter();
XMLHelper.writeNode(authDOM, rspWrt);
deflaterOutputStream.write(rspWrt.toString().getBytes());
deflaterOutputStream.close();
String encodedRequestMessage =
Base64.encodeBytes(byteArrayOutputStream.toByteArray(),
Base64.DONT_BREAK_LINES);
return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim();
}
Thus, this part - "SAMLRequest =" + SAMLRequest - is formed.
"https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO?" +
"SAMLRequest=" + encodedRequestMessage
As for SigAlg. I use http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23dsa-sha1
Therefore, this part also is formed.
"https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO?" +
"SAMLRequest=" + encodedRequestMessage + "&SigAlg=" + SigAlg
Question in forming of this part:
"&Signature=" + Signature
I get a signature so (but this method does not give the signature):
public Signature getSignature() {
KeyStore keyStore =
KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fileInputStream =
new FileInputStream(new File(jksFileName));
keyStore.load(fileInputStream, jksPassword);
fileInputStream.close();
KeyStore.PrivateKeyEntry privateKeyEntry =
(KeyStore.PrivateKeyEntry) keyStore.getEntry(alias,
new KeyStore.PasswordProtection(jksPassword));
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
X509Certificate certificate =
(X509Certificate) privateKeyEntry.getCertificate();
BasicX509Credential credential = new BasicX509Credential();
credential.setEntityCertificate(certificate);
credential.setPrivateKey(privateKey);
Signature signature =
(Signature) org.opensaml.xml.Configuration.getBuilderFactory()
.getBuilder(org.opensaml.xml.signature.Signature.DEFAULT_ELEMENT_NAME)
.buildObject(org.opensaml.xml.signature.Signature.DEFAULT_ELEMENT_NAME);
signature.setSigningCredential(credential);
SecurityConfiguration securityConfiguration =
Configuration.getGlobalSecurityConfiguration();
String keyInfoGeneratorProfile = null;
SecurityHelper.prepareSignatureParams(signature,
credential, securityConfiguration, keyInfoGeneratorProfile);
return signature;
}
In the method buildAuthnRequestObject() is added:
private AuthnRequest buildAuthnRequestObject() {
...
authRequest.setSignature(getSignature()); // <---- to sign the request
return authRequest;
}
This message also compressed and encoded in Base64 and takes the form:
<?xml version="1.0" encoding="UTF-8"?>
<samlp:AuthnRequest AssertionConsumerServiceURL=
"https://ip_of_my_sp_here/EsiaChecker_war/resp/"
Destination=
"https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO"
ForceAuthn="false"
ID="0"
IsPassive="false"
IssueInstant="2014-09-1T19:07:42.718Z"
ProtocolBinding=
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<samlp:Issuer xmlns:samlp=
"urn:oasis:names:tc:SAML:2.0:assertion">my_issuer_here
</samlp:Issuer>
<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="#0">
<ds:Transforms>
<ds:Transform Algorithm=
"http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<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/>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue/>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIIEXD...0CFtd6whg==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</samlp:AuthnRequest>
It is seen that the element SignatureValue is empty.
How can I get the signature using OpenSaml and add it as a GET request parameter? To make it so: "&Signature =" + Signature.

There is a component called HTTPRedirectDeflateEncoder that will help with this, adding the sigalg, encoding and deflating and redirecting the user.
I have an example of usage on my blog, https://blog.samlsecurity.com/2011/01/redirect-with-authnrequest.html
I also have wider examples of its usage in my book, A Guide to OpenSAML

Related

Signature Verification in Java for RSA SHA-256

EDIT - Updated with full SAML
I'm trying to verify a signature I receive from a SAML Response in Java. The SAML Response is validated by samltool.io, but I am having a difficult time with the Signature class in java. No matter what I try, it's returning false. I have properly canonicalized my XML and I am able to successfully generate the correct MessageDigest with what I have in my content String. Hopefully someone can point out what I'm doing wrong. Here is my code:
public class SigVer {
public static void main(String[] args) throws Exception {
System.out.println("Initialized");
String pubKeyStr = "MIIDqDCCApCgAwIBAgIGAYXQr1PhMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTAeFw0yMzAxMjAxOTM2MDlaFw0zMzAxMjAxOTM3MDlaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMgSFX7FkFVNzGyL7EjtIRaJRmj7/Z8diMZyAVy0rBqcIB7aL9Hn3gVefJs09XJiQiuhLVihaD4H1c9RZzS8U089wmef8w0Gc0Bca8jLziAqbzDsph9lQUxoUFCDCyGhamQZvnAk7Rk+PT012ICN/K/eB0q/Un0VEeyTyPhBimfpA2TTe3nC4kjDpKOzQPhjHvjusL0e5VMnDR37iv+yO+VKZ8bDI5WbJPeR2jWh+6EJHUIoNH0XK6Fna4ektpD4vvMSNapjKVKvyFnNoVQGpl/Hc7J0Dh7b/B4veUuYvrFEhj6iAISuD5hM70iL+xaKkf9GLObj8Ay6RTx7kR99WECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAMfjuP24tBNbcEURI9hhbMECVHxfXauVQF1H5z7uWyp5+Pqphw1LApG5KmR/RFDXzePuuVZNFZON27r78w0tixgR5Z0xrwB8Lkdt9LJSwUGNv0dxpdDPFvBoGopjcnIs1zi5MgiTwW/mkYOWJhZDo+E4CheVRyp3H32hvfEG8B+vrEH7CZ8csIK2N4hYiCJP+xktHH6yXDP7fVSWu1CdWDddpyhI0B0Gwo6aBBiyDElsNyVHGwJGT7dnsWvc8bIKP2IPFgE7zxKoARqzV/jAcryyySRiDjbS9kAw84svgWxBJefoaPAtJjhr84o7222QoxMaxi/X5V9ns+S76Wo3A4Q==";
byte[] certBytes = pubKeyStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(Base64.getDecoder().decode(certBytes));
X509Certificate certificate = (X509Certificate)certFactory.generateCertificate(in);
PublicKey publicKey = certificate.getPublicKey();
String content = "<saml2p:Response xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" Destination=\"http://10.100.1.123:8080/identityiq/external/registration.jsf\" ID=\"id130347804157883831058335572\" IssueInstant=\"2023-01-20T19:39:44.049Z\" Version=\"2.0\"><saml2:Issuer xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:entity\">http://www.okta.com/exk81ebtt38SLQ3I65d7</saml2:Issuer><saml2p:Status><saml2p:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"></saml2p:StatusCode></saml2p:Status><saml2:Assertion xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"id1303478041593882583231621\" IssueInstant=\"2023-01-20T19:39:44.049Z\" Version=\"2.0\"><saml2:Issuer Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:entity\">http://www.okta.com/exk81ebtt38SLQ3I65d7</saml2:Issuer><saml2:Subject><saml2:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">matt#revnatech.com</saml2:NameID><saml2:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml2:SubjectConfirmationData NotOnOrAfter=\"2023-01-20T19:44:44.049Z\" Recipient=\"http://10.100.1.123:8080/identityiq/external/registration.jsf\"></saml2:SubjectConfirmationData></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore=\"2023-01-20T19:34:44.049Z\" NotOnOrAfter=\"2023-01-20T19:44:44.049Z\"><saml2:AudienceRestriction><saml2:Audience>http://10.100.1.123:8080/identityiq/</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant=\"2023-01-20T19:39:44.049Z\" SessionIndex=\"id1674243584047.1963093145\"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute Name=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified\"><saml2:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">matt#revnatech.com</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>";
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(content.getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] digest = md.digest();
System.out.println(new String(Base64.getEncoder().encode(digest)));
String signature = "laE5splYJDi8uoMbbQ1tq8bWHPotfZW/aoJ1psWQkuzgc6wS0XS71MIFM9fcZ7rFQEGUWw0GXR6cy/nyDkCaEOH7o+VvKaainpKSktVrAbBtomWIkFx65AsDbHdcN0CZXIz57yRzzfFWjks+fYEHoEM48/HkB8brFxiiyJ17GEd96O7DErlCxen4W874/uOOneEZYBxXtcZnqQylJjgen3C7VyeQATK1AnGba6TK7cdClJjBDapmo/VnWBP6ovH8ZbATdd6u8au1xBqXBu/a+DLwFIdmd2TdDWmT0mkzz/MqdfCtP8W5nOk3UU9ZwfHzSBaQrWH3/mdIgqh8V57BMw==";
try {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update(content.getBytes(java.nio.charset.StandardCharsets.UTF_8));
boolean result = sig.verify(Base64.getDecoder().decode(signature.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
System.out.println("Returning " + result);
} catch (Exception e) {
System.out.println(e);
}
}
}
My SAML Signature block is as follows:
<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:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod>
<ds:Reference URI="#id130347804157883831058335572">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"></ec:InclusiveNamespaces>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>
1jaT9pnWmUCL0+atOcxHZ2yxhL5XOIhhHaK3Oz0gDWY=
</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
laE5splYJDi8uoMbbQ1tq8bWHPotfZW/aoJ1psWQkuzgc6wS0XS71MIFM9fcZ7rFQEGUWw0GXR6cy/nyDkCaEOH7o+VvKaainpKSktVrAbBtomWIkFx65AsDbHdcN0CZXIz57yRzzfFWjks+fYEHoEM48/HkB8brFxiiyJ17GEd96O7DErlCxen4W874/uOOneEZYBxXtcZnqQylJjgen3C7VyeQATK1AnGba6TK7cdClJjBDapmo/VnWBP6ovH8ZbATdd6u8au1xBqXBu/a+DLwFIdmd2TdDWmT0mkzz/MqdfCtP8W5nOk3UU9ZwfHzSBaQrWH3/mdIgqh8V57BMw==
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIDqDCCApCgAwIBAgIGAYXQr1PhMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTAeFw0yMzAxMjAxOTM2MDlaFw0zMzAxMjAxOTM3MDlaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi02MjkxNDMxMjEcMBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMgSFX7FkFVNzGyL7EjtIRaJRmj7/Z8diMZyAVy0rBqcIB7aL9Hn3gVefJs09XJiQiuhLVihaD4H1c9RZzS8U089wmef8w0Gc0Bca8jLziAqbzDsph9lQUxoUFCDCyGhamQZvnAk7Rk+PT012ICN/K/eB0q/Un0VEeyTyPhBimfpA2TTe3nC4kjDpKOzQPhjHvjusL0e5VMnDR37iv+yO+VKZ8bDI5WbJPeR2jWh+6EJHUIoNH0XK6Fna4ektpD4vvMSNapjKVKvyFnNoVQGpl/Hc7J0Dh7b/B4veUuYvrFEhj6iAISuD5hM70iL+xaKkf9GLObj8Ay6RTx7kR99WECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAMfjuP24tBNbcEURI9hhbMECVHxfXauVQF1H5z7uWyp5+Pqphw1LApG5KmR/RFDXzePuuVZNFZON27r78w0tixgR5Z0xrwB8Lkdt9LJSwUGNv0dxpdDPFvBoGopjcnIs1zi5MgiTwW/mkYOWJhZDo+E4CheVRyp3H32hvfEG8B+vrEH7CZ8csIK2N4hYiCJP+xktHH6yXDP7fVSWu1CdWDddpyhI0B0Gwo6aBBiyDElsNyVHGwJGT7dnsWvc8bIKP2IPFgE7zxKoARqzV/jAcryyySRiDjbS9kAw84svgWxBJefoaPAtJjhr84o7222QoxMaxi/X5V9ns+S76Wo3A4Q==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
I have tried decrypting the SignatureValue with the PublicKey to retrieve what's in the DigestValue, but the value I retrieved when doing that didn't match up. This is what I tried when attempting that:
Cipher c = Cipher.getInstance("RSA");
c.init(Cipher.DECRYPT_MODE, publicKey);
System.out.println(new String(Base64.getEncoder().encode(c.doFinal(Base64.getDecoder().decode(signature.getBytes(java.nio.charset.StandardCharsets.UTF_8))))));
Your approach is wrong in two ways.
First, in XMLdsig, which SAML uses, the 'real' signature (here the RSA signature) is not over the data or even the data hash as such. Rather, the hash of the data is placed in Reference.DigestValue, and the publickey signature is computed over the SignedInfo element, which includes that DigestValue, canonicalized. Conversely validation checks both that the signature matches the SignedInfo and that the DigestValue in the (or each) Reference matches the (related) data. See https://www.w3.org/TR/xmldsig-core2/#sec-Processing .
Second, the 'backwards RSA' Cipher in Java -- "encrypting with the privatekey and decrypting with the publickey" -- is not correct RSA signature; this is a mistake made back in the 1990s that has never been corrected, apparently for compatibility. Correct PKCS1v1-style signature, now retronymed RSASSA-PKCS1-v1_5 as referenced in that same spec, is four steps:
hash the data
encode the hash (1) in an ASN.1 DigestInfo
pad (2) with 01 FF... 00
take (3) as an integer and modexp with the private exponent
Verification can be done by repeating 1-3 and comparing to the result of modexp-public which reverses 4 OR reversing 4 and 3 (remove 01 ...) AND 2 (split apart the ASN.1) before comparing to 1. Java's backwards Cipher only does 3 and 4 on the 'encrypt' side and 4 and 3 on the 'decrypt' side leaving you to do 2, as well as 1 on the SignedInfo per above.
This part is a very common mistake; see my list over at https://crypto.stackexchange.com/questions/87006/why-is-data-signed-with-sha256-rsa-pkcs-and-digest-signed-with-rsa-pkcs-different plus
java certificate verify failed
How to use x509 public key to verify a signed/hashed Alexa request body?

PDFBox : Signature fails "Can't write signature, not enough space"

I implemented a signature feature in JAVA using PDFBox.
The signing part of my code is :
ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(output);
byte[] cmsSignature = new byte[0];
try {
Certificate[] certificationChain = SignatureUtils.getCertificateChain(alias);
X509Certificate certificate = (X509Certificate) certificationChain[0];
PrivateKey privateKey = SignatureUtils.getSignaturePrivateKey(alias, password);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
.build(sha1Signer, certificate));
gen.addCertificates(new JcaCertStore(Arrays.asList(certificationChain)));
CMSProcessableInputStream msg = new CMSProcessableInputStream(externalSigning.getContent());
CMSSignedData signedData = gen.generate(msg, false);
if (tsaUrl != null && !tsaUrl.isEmpty()) {
ValidationTimeStamp validation;
validation = new ValidationTimeStamp(tsaUrl);
signedData = validation.addSignedTimeStamp(signedData);
}
cmsSignature = signedData.getEncoded();
if (logger.isDebugEnabled()) {
logger.debug("signature length = " + cmsSignature.length);
logger.debug("certificate = " + certificate.toString());
}
} catch (GeneralSecurityException | CMSException | OperatorCreationException | IOException e) {
throw new SignatureException(e.getMessage());
}
externalSigning.setSignature(cmsSignature);
Everything works fine if I use my testing auto-signed certificate I generated with the keytool command.
The problem is, when I try this code with an existing certificate which is truly certified, I get the exeption :
Caused by: java.io.IOException: Can't write signature, not enough space
at org.apache.pdfbox.pdfwriter.COSWriter.writeExternalSignature(COSWriter.java:797)
at org.apache.pdfbox.pdmodel.interactive.digitalsignature.SigningSupport.setSignature(SigningSupport.java:48)
I have no idea why this doesn't work...
Any help would be appreciated ! =)
In a comment you mention that you get
signature length = 10721
in the log files and that you
didn't set any preferred size
By default PDFBox reserves 0x2500 bytes for the signature. That's 9472 bytes decimally. Thus, just like the exception says, there's not enough space.
You can set the size PDFBox reserves for the signature by using a SignatureOptions object during document.addSignature:
SignatureOptions signatureOptions = new SignatureOptions();
signatureOptions.setPreferredSignatureSize(20000);
document.addSignature(signature, signatureOptions);

How to verify the digital signature of downloaded xml file in java

I have an XML file, given below
<OfflinePaperlessKyc referenceId="686820201209011401091">
<UidData>
<Poi dob="21-01-1945" e="bd05902a34b7b9e4d92230ac6b0asded072c1c0945190ad357f9ceec387f6fd6" gender="M" m="ed4eeb1376575c1b59096cf5c41243re5f3d4146e0c63eb4cc08a6c22fa9afe5" name="Reepak Sangid"/>
<Poa careof="S/O John Uncle" country="India" dist="Jaipur" house="1134 - B" landmark="80 Feet Road 200 Feet Baypass ke pass" loc="Heerapura Ajmer Road" pc="302024" po="" state="Kerala" street="Heera Nagar" subdist="" vtc="Jaipur"/>
</UidData>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>+ribhQOajFYDHgBTkjU0LSHWxRWpBkkrh4FbUj+RVss=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>qGxO1EURe+FOr0mBocWvBDGuZm3ssVu6dYwSs5IvuA2OhlqH+Cp2WSQ3OKFvLZPfVSXBDNy/zC2F M5NI7RJjl2KaCMX+Qhko7UTpQp/8a/gX3jjcRP5Az8w4GQONIuZhLjUXxqgA4ln0y8vqfEhqdtVk 407QopDyNx6UH9VFCgmGjcDggLUgam1gG4P8Hka3WxT0BFTL7CWO4yVPRwEs8IvhcNvm8Db7p50a qWMwfOmNQQ/G7uj+tQ2wEKzWPcBoeQ6/qGCGfN+1Wt0gmEWIxYaKEDz9kQb8eiYOQSaht+2QzOX7 68gMrDXSms6ljrcklqmVrXwKvJXe5L0tfbNBuA==</SignatureValue>
</Signature>
</OfflinePaperlessKyc>
Here some details about signature:
signature - Signature: – This will a 344 character long digital signature of the data present in the downloaded XML. This can be validated using the public key of UIDAI which will be present in standard signed XML.
Here are the steps provided by the website from which the XML is downloaded:
Steps to validate the signature :
Read the entire XML.
Get signature from XML
Get Certificate from here.
Convert the certificate to base64 string.
Sample code snippets provided here.
Here is the text in the certificate file
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
How can I verify the digital signature provided in the XML file using the certificate file. I have searched many questions but they did not work for me. Thanks in advance.
Here is what I tried:
String certificate = "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";
byte[] decoded = Base64.getDecoder().decode(certificate.getBytes());
String signatureValue = "qGxO1EURe+FOr0mBocWvBDGuZm3ssVu6dYwSs5IvuA2OhlqH+Cp2WSQ3OKFvLZPfVSXBDNy/zC2F M5NI7RJjl2KaCMX+Qhko7UTpQp/8a/gX3jjcRP5Az8w4GQONIuZhLjUXxqgA4ln0y8vqfEhqdtVk 407QopDyNx6UH9VFCgmGjcDggLUgam1gG4P8Hka3WxT0BFTL7CWO4yVPRwEs8IvhcNvm8Db7p50a qWMwfOmNQQ/G7uj+tQ2wEKzWPcBoeQ6/qGCGfN+1Wt0gmEWIxYaKEDz9kQb8eiYOQSaht+2QzOX7 68gMrDXSms6ljrcklqmVrXwKvJXe5L0tfbNBuA==";
byte[] aadharSignature = signatureValue.getBytes();
//Creating KeyPair generator object
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair pair = keyPairGen.generateKeyPair();
PrivateKey privKey = pair.getPrivate();
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(privKey);
signature.update(aadharSignature);
//Initializing the signature
signature.initVerify(pair.getPublic());
signature.update(decoded);
//Verifying the signature
boolean bool = signature.verify(decoded);
if(bool) {
System.out.println("Signature verified");
} else {
System.out.println("Signature failed");
}
But this does not work and generates an error while generating the key.

Unsupported Signature Method sha256-rsa-MGF1 for XML digital signature validation

I'm doing XML digital signature validation, but the algorithm SHA256WITHRSAANDMGF1 is not supported.
Here's the related part in XML file:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="cXMLSignature">
<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#sha256-rsa-MGF1" />
<ds:Reference URI="#cXMLSignedInfo">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>dafdfadfdfaddfadfefdafdfdfadfdaf=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
In the 4th line of the above xml, the signature method algorithm is:
<ds:SignatureMethod Algorithm="http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1" />
I tried something like below:
BouncyCastleProvider bc = new BouncyCastleProvider();
Security.addProvider(bc);
//...
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = fac.unmarshalXMLSignature(validateContext);
But got error:
unsupported SignatureMethod algorithm: http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1
So I added a new line like below:
fac.newSignatureMethod("http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1", null);
But it also fails.
It seems that I need to implement the interface SignatureMethodParameterSpec for SHA256WITHRSAANDMGF1 algorithm, but I'm not familiar with this implementation. Do you have any suggestions for this implementation? Or examples for it? Thanks!
This issue is also has the same problem: unsupported SignatureMethod algorithm, but the algorithm is listed as available Service by BC-Provider
Thanks!
Update by myself:
This validation could be done by another library: https://santuario.apache.org/
Here's the sample code:
// Configure for unsupported algorithm
BouncyCastleProvider bc = new BouncyCastleProvider();
Security.addProvider(bc);
org.apache.xml.security.Init.init();
// Load document
File f = new File("C:/Archive/test.xml");
InputStream inputStream = new FileInputStream("C:/Archive/test.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream);
// Public Key
NodeList x509certNodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, "X509Certificate");
String x509CertText = x509certNodeList.item(0).getTextContent();
byte[] x509CertBytes = Base64.getDecoder().decode(x509CertText.getBytes());
System.out.println("x509 Cert Text: " + x509CertText);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(new ByteArrayInputStream(x509CertBytes));
PublicKey publicKey = cert.getPublicKey();
System.out.println("x509 Cert Public-Key Algorithm: " + publicKey.getAlgorithm());
System.out.println("x509 Cert Public-Key Format: " + publicKey.getFormat());
// Signature Value
NodeList signValueNodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, "SignatureValue");
String sigValue = signValueNodeList.item(0).getTextContent();
System.out.println("SignatureValue: " + sigValue);
// Validate
NodeList signatureNodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
Node signatureNode = signatureNodeList.item(0);
System.out.println("Signature Node Local Name: " + signatureNode.getLocalName());
System.out.println("f: " + f.toURI().toURL().toString());
org.apache.xml.security.signature.XMLSignature signature1 = new org.apache.xml.security.signature.XMLSignature(
(Element)signatureNode, f.toURI().toURL().toString()
);
boolean result = signature1.checkSignatureValue(publicKey);
System.out.println("result: " + result);

how to get certificate chain (root and intermediates) from an XMLSignature

Hi I just have contructed an org.apache.xml.security.signature.XMLSignature from a xml that complies with xmldsig w3c recommendation, and I can see that the xml contains all certificate chain in
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
elements, but using the XMLSignature API I can see that I can only access the user certificate and the issuer one, but not the complete chain, is there an straightforward way to get this done through xmlsec API?
I found a solution for this, not the most clean, but it works:
XMLSignature signature = new XMLSignature(sigElement,
null);
KeyInfo keyInfo = signature.getKeyInfo();
NodeList x509Certificates = keyInfo.getElement().getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "X509Certificate");
ArrayList<X509Certificate> allCertificates = new ArrayList<X509Certificate>();
for (int i = 0; i < x509Certificates.getLength(); i++) {
Node x509CertificateElement = x509Certificates.item(i);
byte[] decodedX509Certificate = Base64.decode(x509CertificateElement.getTextContent());
X509Certificate x509Certificate = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(decodedX509Certificate));
allCertificates.add(x509Certificate);
}
// now you have all certificates in allCertificates

Categories