XML Encryption using Java Callout Apigee-Java-XMLDSIG

Hi Team,

Please help me to understand how can I use the Java Callout XML payload signing  using Public certificate (x509 certificate)

I tried the signing with an example given in the https://github.com/DinoChiesa/Apigee-Java-XMLDSIG/tree/master But when I am trying to call the different setting for Certificate it is failing.

This is what I am using in my Java callout Policy 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout name="Java-XMLDSIG-Sign">
<Properties>
<Property name="source">message.content</Property>
<Property name="output-variable">output</Property>
<Property name="certificate">{cert}</Property>
<Property name="key-identifier-type">X509_CERT_DIRECT</Property>
</Properties>
<ClassName>com.google.apigee.callouts.xmldsig.Sign</ClassName>
<ResourceURL>java://apigee-xmldsig-20210409.jar</ResourceURL>
</JavaCallout>

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout name="Java-XMLDSIG-Sign">
<Properties>
<Property name="source">message.content</Property>
<Property name="output-variable">output</Property>
<Property name="certificate">{cert}</Property>
<Property name="key-identifier-type">X509_CERT_DIRECT</Property>
</Properties>
<ClassName>com.google.apigee.callouts.xmldsig.Sign</ClassName>
<ResourceURL>java://apigee-xmldsig-20210409.jar</ResourceURL>
</JavaCallout>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout name="Java-XMLDSIG-Sign">
<Properties>
<Property name="source">""</Property>
<Property name="output-variable">output</Property>
<Property name="certificate">message.content</Property>
<Property name="key-identifier-type">X509_CERT_DIRECT</Property>
<Property name="private-key">{my_private_key}</Property>
<Property name="private-key-password">{my_private_key_password}</Property> 
</Properties>
<ClassName>com.google.apigee.callouts.xmldsig.Sign</ClassName>
<ResourceURL>java://apigee-xmldsig-20210409.jar</ResourceURL>
</JavaCallout>

and I am getting an error which says "java.lang.IllegalStateException: private-key resolves to an empty string" which means its in not reading the properties the way I am passing and other case I am getting certificate is missing.

@dchiesa1 @anilsr  Can you please help me with the correct properties to be passed to achieve my goal.

Goal is to use signing  with Cert of Format x509, How can i achieve signing the XML payload using certificate instead of Key

Solved Solved
0 20 712
4 ACCEPTED SOLUTIONS

Oh I see. Thanks for that screenshot. It looks like you are using Postman to send the POST to the /validate2 API.  That will work just fine, but you need to be careful with the format of the data you are passing in. Specifically, you need to insure there is no added whitespace.

With XMLDSIG, you cannot "reformat" or "pretty print" the signed XML, after the signature has been applied. You cannot change the block that has been signed, nor can you change the SignedInfo element that is added in the signing process. If you make such changes, the validation of signatures will fail. This applies to XML Signature in general, not to this particular callout that you can use within Apigee. This is just how XML Signature works. More information on this is here

From the screenshot I see that the payload in your Postman window seems to be pretty-printed and reformatted. Specifically I see added newlines and modified indentation in the SignedInfo element. This will cause the validation of the signature to fail. That is why you are seeing only "false" in your trials.

If you send in an XML document that is EXACTLY the content that is provided in the sample-data directory,  you'll see that the signature validation will succeed. 

As for XML encryption, that's a different topic.  If you can tell me precisely what you need to accomplish, we can try to find a solution. That might involve updating the xml cipher callout. 

View solution in original post

ps: I updated the callout, There is now a reform-signedinfo property that will try to fixup the SignedInfo element, removing spaces and newlines. This sometimes allows the validation of the signature to succeed, in cases like yours in which you have altered the signed document. It will not fixup problems with modified whitespace in the signed document itself. Check the readme of the callout for more information on this new option.

View solution in original post

Hey Abishek
The XML Digital Signature callout should be able to validate documents signed in the form you've shown. 

There are a variety of ways that a signer can embed Key information into the signed document: Embedding the RSA key value directly into the signed document; Embedding the Certificate into the signed document; Embedding the issuer serial number and issuer name.  

The example you showed uses two options, embedding the X509 cert directly into the signed document, and also embedding the serial number and issuer name.  The callout will be able to extract the certificate from the signed document. To use that option, you need to specify the SHA1 thumbprint of the acceptable certificate, in the configuration for the callout. This tells the callout "you can trust signed documents if they use THIS certificate.

The configuration for that would look something like this: 

 

<JavaCallout name='Java-XMLDSIG-Validate'>
  <Properties>
    <Property name='source'>message.content</Property>
    <Property name='key-identifier-type'>x509_cert_direct</Property>
    <Property name='certificate-thumbprint'>{sha1-thumbprint-of-acceptable-cert}</Property>
  </Properties>
  <ClassName>com.google.apigee.callouts.xmldsig.Validate</ClassName>
  <ResourceURL>java://apigee-xmldsig-20220916.jar</ResourceURL>
</JavaCallout>

 

It should just work. 

As of today (latest revision), the policy validates that the signature applies to the root element of the document.  So you should get the latest.  Try it and see.

View solution in original post

Sure, I've updated the callout so that it can now generate a signed document of the form you showed. Before this update, the callout could validate a document of that form, though there were no tests for that.  So I also added tests into the source tree for that case. 

and, I've updated the example proxy to show how to configure the callout to sign with an embedded cert, and also how to configure it to validate via an embedded cert.  In the latter case you must specify a thumbprint, or a list of thumbprints, of certificates the callout should trust.  I've updated the README to describe those example flows.

All of this is available now in version 20220920. 

 

View solution in original post

20 REPLIES 20

The callout that you are referencing, Apigee-Java-XMLDSIG, does XML Digital Signatures, aka XMLDSIG. That means signing and verifying signatures. It does not perform encryption. Encryption is something different from signing. 

When you digitally sign a document using an asymmetric algorithm (like RSA), you provide the private key.  The verifier of the signature will use a public key, which can be provided via a certificate.  This is stated in the README for that callout. 

It seems that you are asking the callout to SIGN a document. But you are providing a certificate.  The cert is a source of a public key, not a private key.  That won't work.  The error that you observe, "private-key resolves to an empty string", is telling you that you haven't specified a private key. The README for the callout states that this is a required parameter when signing. 

So, if you want to sign, provide a private key. Check the README for full details.

The title of your post uses the phrase "XML Encryption" , but the text of your post says you are using the callout that performs signing. Encryption and signing are not the same thing. 

If you really want XML encryption, there is a separate callout.  https://github.com/DinoChiesa/Apigee-Java-XmlCipher .  But that uses a symmetric algorithm. It will not accept an RSA public key (via a cert) as the source of the encrypting key.  That's an open feature request for that callout!

Thanks. @dchiesa1 for your quick response  So I have two steps one is to decrypt the payload with my Private key and then to verify the signature with client's public cert what is the approach i should follow.

I have two steps one is to decrypt the payload with my Private key and then to verify the signature with client's public cert what is the approach i should follow.

I understand what you're writing here, but as I told you earlier, this callout does not perform decryption or encryption. It is signing and verification. The callout that you are referring to will be helpful only with verifying the xml digital signature.

Hi @dchiesa1  I have tried the example given in Repo as well it works fine as it is during signing but giving false for both the cases.

/sign1 works fine

/sign2 works fine

/validate1 return false

/validate2 returns false 

Can we have one scenario in which we get True. 

That's odd.  When I try exactly the steps described in the README, it works for me. 

I don't know how to help you resolve that problem.  Maybe start over and keep things exactly as they are in the sample bundle, and retry?

I have tried it again, same result. Screen Shot 2022-09-16 at 11.49.28 AM.png

 

Also I got your point so i can use to different callouts to achieve each task which is fine as well. But I saw https://github.com/DinoChiesa/Apigee-Java-XmlCipher does not support RSA based encryption and decryption is it possible to get it done somehow ?? How can I achieve that, Please help.

Oh I see. Thanks for that screenshot. It looks like you are using Postman to send the POST to the /validate2 API.  That will work just fine, but you need to be careful with the format of the data you are passing in. Specifically, you need to insure there is no added whitespace.

With XMLDSIG, you cannot "reformat" or "pretty print" the signed XML, after the signature has been applied. You cannot change the block that has been signed, nor can you change the SignedInfo element that is added in the signing process. If you make such changes, the validation of signatures will fail. This applies to XML Signature in general, not to this particular callout that you can use within Apigee. This is just how XML Signature works. More information on this is here

From the screenshot I see that the payload in your Postman window seems to be pretty-printed and reformatted. Specifically I see added newlines and modified indentation in the SignedInfo element. This will cause the validation of the signature to fail. That is why you are seeing only "false" in your trials.

If you send in an XML document that is EXACTLY the content that is provided in the sample-data directory,  you'll see that the signature validation will succeed. 

As for XML encryption, that's a different topic.  If you can tell me precisely what you need to accomplish, we can try to find a solution. That might involve updating the xml cipher callout. 

ps: I updated the callout, There is now a reform-signedinfo property that will try to fixup the SignedInfo element, removing spaces and newlines. This sometimes allows the validation of the signature to succeed, in cases like yours in which you have altered the signed document. It will not fixup problems with modified whitespace in the signed document itself. Check the readme of the callout for more information on this new option.

Hi @dchiesa1  I see now seems like the issue is because of Postman, when i tried calling the curl its working fine, But how can I test it with postman or SOAPUI being a standard tools for API Testing.

Also in not sure whether the above solution will help me to verify the signed payload from customer which looks something like below do we support this kinda of signing and Validation in callout I would be very greatful if you can provide some insights on this Please 🙂 thats the actual used case I want to achieve using callout policy, verifying signature for the below Payload.

<Details>
<Name></Name>
<Email></Email>
<Contact></Contact>
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="" />
<ds:SignatureMethod Algorithm="" />
<ds:Transforms>
<ds:Transform Algorithm=""/>
<ds:Transform Algorithm=""/>
</ds:Transforms>
<ds:DigestMethod Algorithm="" />
<ds:DigestValue>********</ds:DigestValue>
</ds:SignedInfo>
<ds:SignatureValue>########</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>@@@@</ds:X509IssuerName>
<ds:X509SerialNumber>%%%%</ds:X509SerialNumber>
</ds:X509issuerSerial>
<ds:X509Certificate>
********
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</Details>

I am not sure if its correct way of doing things but thats the used case we have and I am not sure how to achieve it using apigee. 

 

Hey Abishek
The XML Digital Signature callout should be able to validate documents signed in the form you've shown. 

There are a variety of ways that a signer can embed Key information into the signed document: Embedding the RSA key value directly into the signed document; Embedding the Certificate into the signed document; Embedding the issuer serial number and issuer name.  

The example you showed uses two options, embedding the X509 cert directly into the signed document, and also embedding the serial number and issuer name.  The callout will be able to extract the certificate from the signed document. To use that option, you need to specify the SHA1 thumbprint of the acceptable certificate, in the configuration for the callout. This tells the callout "you can trust signed documents if they use THIS certificate.

The configuration for that would look something like this: 

 

<JavaCallout name='Java-XMLDSIG-Validate'>
  <Properties>
    <Property name='source'>message.content</Property>
    <Property name='key-identifier-type'>x509_cert_direct</Property>
    <Property name='certificate-thumbprint'>{sha1-thumbprint-of-acceptable-cert}</Property>
  </Properties>
  <ClassName>com.google.apigee.callouts.xmldsig.Validate</ClassName>
  <ResourceURL>java://apigee-xmldsig-20220916.jar</ResourceURL>
</JavaCallout>

 

It should just work. 

As of today (latest revision), the policy validates that the signature applies to the root element of the document.  So you should get the latest.  Try it and see.

@dchiesa1 Thankyou so much for your response I will give it a try but the only problem right now is I can not replicate the same behaviour even for testing, Is it possible that we can sign the document and generate similar kinda of payload as given in example above and then I can validate it against the certitificate. If that easy to append the same functionality for an example purpose to be added to proxy bundle that would be great help. 

If you can give me a sample payload, as above, but complete with all data (and unmodified), then I would be glad to test it out with the callout. Your sample should be signed with a test key, and should embed a test certificate.  Please do not send me a sample that uses your production key & cert.

@dchiesa1 Thats the problem otherwise I would pasted the exact payload it has some information regarding the Client(CN value has a name of project I am working on) and because it as POC I want be able to provide you even the testing sample, Is it possible anyways we can generate something similar with some random values and dummy certificates and keys ? 

is it okay if I will send you the data removing the Issuername and Serial Number would it be still valid to test ??

Yes, you can send it that way. But it is probably pointless to do so. The signed message will include the X509Certificate element; any reader that can parse the X509Certificate element will be able to retrieve the full certificate, which will include the issuer name and the Serial number. 

If you are concerned, you can get a test certificate for free from LetsEncrypt. 

 

Yeah got your point 🙂 but unfortunately I cant, Cant we generate or create an output like below using our certificate we get from LetsEncrypt ???  Like if any utility can create the below output for us and then we can test it, let me check if something I can find online and I really appreciate your quick response in this matter

<Details>
<Name></Name>
<Email></Email>
<Contact></Contact>
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="" />
<ds:SignatureMethod Algorithm="" />
<ds:Transforms>
<ds:Transform Algorithm=""/>
<ds:Transform Algorithm=""/>
</ds:Transforms>
<ds:DigestMethod Algorithm="" />
<ds:DigestValue>********</ds:DigestValue>
</ds:SignedInfo>
<ds:SignatureValue>########</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>@@@@</ds:X509IssuerName>
<ds:X509SerialNumber>%%%%</ds:X509SerialNumber>
</ds:X509issuerSerial>
<ds:X509Certificate>
********
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</Details>

Cant we generate or create an output like below using our certificate we get from LetsEncrypt ???

Yes, I hope you can do that.

I could generate a signed document that looks like that, and then validate it with the callout. But that would not be a very useful test, because it's my code that would generate the signed document.  We would be verifying only that my code is interoperable with my code. That's not useful for you. 

A useful test would be if you use YOUR system to generate the signed document, using a test key and certificate. Then send to me that signed document, and I'll verify that the Validate callout can validate the signature. It's important that the signed document be generated from your system, so that we can have some assurance that your system (whatever it is) and the Validate callout are interoperable.

So I think the steps for you are:

  • provision a cert from LetsEncrypt (or use openssl to generate a key and self-signed cert)
  • configure your system to use that test key to generate a signed document
  • send to me the signed document.

Then I will test it.

@dchiesa1 Thanks Dino,

I could generate a signed document that looks like that, and then validate it with the callout. But that would not be a very useful test, because it's my code that would generate the signed document. We would be verifying only that my code is interoperable with my code. That's not useful for you.

Can we do that as well?? It would give me a better idea how things work.

As I have some dependencies on other teams to make some change even if its a certs change from there end, I will try to get that for you but I meanwhile if we can have a similar used case and we can get it validated I would have at least something to understand and look forward that yes it works is similar fashion I am expecting.

Sure, I've updated the callout so that it can now generate a signed document of the form you showed. Before this update, the callout could validate a document of that form, though there were no tests for that.  So I also added tests into the source tree for that case. 

and, I've updated the example proxy to show how to configure the callout to sign with an embedded cert, and also how to configure it to validate via an embedded cert.  In the latter case you must specify a thumbprint, or a list of thumbprints, of certificates the callout should trust.  I've updated the README to describe those example flows.

All of this is available now in version 20220920. 

 

Thanks @dchiesa1  I will try this onto my system and see if it validates the Payload, Really appreciate your help and support.