Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

An Apigee custom policy implemented in Java that signs and verifies SOAP / WS-Security messages. THIS NO LONGER WORKS because changes in the permission model for Apigee prevent this code from loading.

License

Notifications You must be signed in to change notification settings

DinoChiesa/ApigeeEdge-Java-WsSec-Signature

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WS-Sec Signature

This directory contains a maven project and the Java source code required to compile a simple custom policy for Apigee Edge. The policy signs a SOAP document using a signature based on an X.509 certificate, or verifies a SOAP document that has been so signed. The implementation uses WSS4J, the well-known Java library for WS-Security.

Status

THIS NO LONGER WORKS.

Due to a permissions change in Apigee Edge runtime, this callout won't initialize. https://community.apigee.com/questions/59204/ws-security-signature-verification.html

Good news: there's a replacement available for this callout. Find it here.

Old Notes

For example, suppose you have a document like this:

 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header/>
  <soap:Body>
    <act:test xmlns:act="http://yyyy.com">
      <abc>
        <act:demo>fokyCS2jrkE5s+bC25L1Aax5sK....08GXIpwlq3QBJuG7a4Xgm4Vk</act:demo>
      </abc>
    </act:test>
  </soap:Body>
</soap:Envelope>

You can sign the body so that it results in:

 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soap:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">
      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-1754a532-c1c5-42c1-b195-4a36b31ce9c9">
        <ds:SignedInfo>
          <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
            <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap xsd xsi"/>
          </ds:CanonicalizationMethod>
          <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
          <ds:Reference URI="#id-3ddfa6e3-a96c-4b42-a1bb-8601ddfc36d0">
            <ds:Transforms>
              <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="xsd xsi"/>
              </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            <ds:DigestValue>ezKnouSbVtsfuA+WA4abdOiBYOs=</ds:DigestValue>
          </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>K4ZfcgVxthz0egyUbAkxqVFS04yWOcxSqOvkp+S3Wtcfe8wRMcGBx6gaaXMmz1IJXQIniPRwvRZF&#13;
301pQU9LsjHtHt2UBp1/qJt4HmguBnW54hYr2pUCcXf1DXeQcPS/05/iaOw99A0QHKGdqZRtgLGt&#13;
kJygLc3Wt14jy+DI10Cc3SaOwPgCWxdj3D+2E4ntnsMC+MgkuNXEzKp3gfdrZBy1I4gBoK4l1wGT&#13;
MkT5HJUL9sbgkGfBOd6fEs3in1YqgVDoWJLMgXv+l4WcEJQx1+EWWfWbB+cVueNVh83YyBWQKa+X&#13;
Df5F+vmR9pnA1oJkJJA6O2qVOtezyfZAuvrnuQ==</ds:SignatureValue>
        <ds:KeyInfo Id="KI-c3feb85a-8dab-4585-8f44-6812bf440b91">
          <wsse:SecurityTokenReference wsu:Id="STR-8a805657-add6-475a-aef7-b5eb2fb7fd15">
            <ds:X509Data>
              <ds:X509IssuerSerial>
                <ds:X509IssuerName>CN=SOADEV,OU=Dino,O=Chiesa,L=WVC,ST=WA,C=US</ds:X509IssuerName>
                <ds:X509SerialNumber>1453916936</ds:X509SerialNumber>
              </ds:X509IssuerSerial>
            </ds:X509Data>
          </wsse:SecurityTokenReference>
        </ds:KeyInfo>
      </ds:Signature>
    </wsse:Security>
  </soap:Header>
<soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-3ddfa6e3-a96c-4b42-a1bb-8601ddfc36d0">
    <act:test xmlns:act="http://yyyy.com">
      <abc>
        <act:demo>fokyCS2jrkE5s+bC25L1Aax5sK....08GXIpwlq3QBJuG7a4Xgm4Vk</act:demo>
      </abc>
    </act:test>
  </soap:Body>
</soap:Envelope>

Conversely, if you have a SOAP document that looks like the latter, you can verify the signature, and obtain the former, unsigned document.

Disclaimer

This example is not an official Google product, nor is it part of an official Google product.

Using this Custom policy JAR

To use this JAR in Apigee Edge, you do not need to rebuild it.

JAR dependencies

In your API Proxy you must supply this jar, and all of the its dependencies. The list of jars that must appear in your resources/java directory (within the API Proxy bundle) is:

  • commons-lang3-3.7.jar
  • edge-wssec-sign-x509-1.0.4.jar
  • wss4j-bindings-2.2.1.jar
  • wss4j-ws-security-common-2.2.1.jar
  • wss4j-ws-security-dom-2.2.1.jar
  • wss4j-ws-security-stax-2.2.1.jar
  • xmlsec-2.1.1.jar

Configuring

You will need to configure the Callout policy with the appropriate information. You will need: a java key store (.jks file) containing the key to use to sign. The implementation uses Apache's WSS4J. As such, it relies on a Java Key Store for th ekey.

This jar has a "compiled in", default .jks file. It will work for demonstration purposes, but you probably don't want to use the sample .jks file to sign and verify your messages in a real working system.

Think of the .jks as a signing key, or a verification key. You will want your own key, for doing signing and verification.

Simple Example Configuration: Signing

A simple configuration looks like this:

<JavaCallout name='Java-SignSoapDoc'>
  <Properties>
    <Property name='alias'>apigee</Property>
    <Property name='password'>Secret123</Property>
    <!-- the above really should be private variables retrieved from KVM:
      <Property name='alias'>{private.alias}</Property>
      <Property name='password'>{private.password}</Property>
    -->
  </Properties>
  <ClassName>com.google.apigee.callout.wssec.SOAPSigner</ClassName>
  <ResourceURL>java://edge-wssec-sign-x509-1.0.5.jar</ResourceURL>
</JavaCallout>

The parameters for the policy should be clear:

  • The alias is the "key alias"
  • the password is the password on the key.

This configuration uses the "compiled-in" .jks file, to sign a payload. As stated above, this is probably not what you want. The compiled-in .jks is a sample key, not suitable for production use!

A better configuration explicitly specifies the .JKS file to the callout.

<JavaCallout name='Java-SignSoapDoc-BYOJKS'>
  <Properties>
    <Property name='alias'>myalias</Property>
    <Property name='password'>mypassword</Property>
    <!-- this is the base64-encoded JKS file, generated with
         -->
    <Property name='jks-base64'>
      /u3+7QAAAAIAAAACAAAAAQAGc29hZGV2AAABUoQ0biUAAAUCMIIE/jAOBgorBgEE
      ASoCEQEBBQAEggTq3Nh2qAwD/cp3TB7dfy5GJRCSomjR0cWHPAeD/wHZDjiZYbBC
      XB2sByHfcogXKY5uIVfd7pD2DTCK3eFeYNnEQi0vhI2yGYbnwnX3GCOTpEv5ty1p
      xh1O+gQfm+GLCekeSOyjE2gt+TWC9Jt0VBHz87gn7dLyfTowEdPEo40/t9KJHgAd
      t6JhjomFwDjO5kTHvtwyWAl5taGRwgtXspGiuwgvD+9f9hKsqNGXijAocUjr3RFz
      BpyEHAtO8WBrFyRsHgu3hk1U++e4ncpMvxOyr2Jo93rSRyTp3VqmQRRHVSaoCRH8
      ....
    </Property>
  </Properties>
  <ClassName>com.google.apigee.callout.wssec.SOAPSigner</ClassName>
  <ResourceURL>java://edge-wssec-sign-x509-1.0.5.jar</ResourceURL>
</JavaCallout>

The additional parameter there, specifies the data for the java keystore.

The base64 string that appears in the configuration is the result of base64-encoding a .jks file. To get that, you need to do 2 things.

  1. generate a JKS
  2. base64 encode it.

To generate, follow Oracle's instructions using the keytool.

keytool -genkey -v -keystore my-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias

On MacOS, you can do the encoding of the resulting .jks file like this:

 openssl base64 -in my-keystore.jks -out jks.bin

On other platforms you can use other similar utilities. For example on Windows, use Powershell and the Convert.ToBase64String() method on the byte contents of the .jks file.

When you specify the base64-encoded JKS in the configuration, the sample .jks that is compiled into the JAR is not used to sign the payload. Instead the .jks specified by the base64 string is used.

Because the alias, password, and JKS file are secrets, the best practice is to store those items in the encrypted KVM inside Apigee Edge. At runtime (in the API Proxy), retrieve the data from the KVM, and reference them by variable in the signing policy, like this:

<JavaCallout name='Java-SignSoapDoc-BYOJKS'>
  <Properties>
    <Property name='alias'>{private.keyalias}</Property>
    <Property name='password'>{private.keypassword}</Property>
    <Property name='jks-base64'>{private.jks-base64}</Property>
  </Properties>
  <ClassName>com.google.apigee.callout.wssec.SOAPSigner</ClassName>
  <ResourceURL>java://edge-wssec-sign-x509-1.0.5.jar</ResourceURL>
</JavaCallout>

There is an additional property you can specify if appropriate: jks-password. Use it like this:

    <Property name='jks-password'>{private.jks-password}</Property>

You would use this if the .jks file itself is protected by a password.

Note: This policy transforms the message. When performing signing, the output of message.content will be the SOAP message with a signature.

Simple Example Configuration: Verifying

A simple configuration looks like this:

<JavaCallout name='Java-VerifySignature'>
  <Properties>
    <Property name='alias'>{private.keyalias}</Property>
    <Property name='password'>{private.keypassword}</Property>
    <Property name='jks-base64'>{private.jks-base64}</Property>
  </Properties>
  <ClassName>com.google.apigee.callout.wssec.SOAPVerifier</ClassName>
  <ResourceURL>java://edge-wssec-sign-x509-1.0.5.jar</ResourceURL>
</JavaCallout>

Note the different classname: SOAPVerifier not SOAPSigner.

This configuration uses parameters set into private variable, to verify a payload. See the notes above for a discussion of extracting those things from the Apigee Edge KVM.

Note: This policy transforms the message. When using verification, the output of message.content will be stripped of the WS-Security header.

Example API Proxy

You can find an example proxy bundle that uses the policy, here in this repo.

To use it, you can deploy it with the importAndDeploy tool in the tools directory:

cd tools
ORG=myorgname
ENV=myenvname
node ./importAndDeploy.js -v -n -o $ORG -e $ENV -d ../example-bundle/

You can also provision a JKS into the KVM, to be used by the example proxy, with a tool in that directory:

node ./provisionKvm.js -F larry-page.jks -A my-key-alias -P Secret123 -o $ORG -e $ENV -v -n -f

Invoke it like this:


Building

You don't need to build this JAR from source. If you want to, building from source requires Java 1.8, and Maven.

  1. unpack (if you can read this, you've already done that).

  2. Before building the first time, configure the build on your machine by loading the Apigee jars into your local cache:

./buildsetup.sh
  1. Build with maven.
mvn clean package

This will build the jar and also run the tests.

Support

This callout is open-source software, and is not a supported part of Apigee Edge. If you need assistance, you can try inquiring on The Apigee Community Site. There is no service-level guarantee for responses to inquiries regarding this callout.

License

This material is Copyright 2018 Google LLC. and is licensed under the Apache 2.0 License. This includes the Java code as well as the API Proxy configuration.

Bugs

  • The tests could be more rigorous

About

An Apigee custom policy implemented in Java that signs and verifies SOAP / WS-Security messages. THIS NO LONGER WORKS because changes in the permission model for Apigee prevent this code from loading.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published