When using WSS4J in a Java callout, crypto object doesn't get initialized

We are trying to decrypt an encrypted xml payload using crypto and facing issues with loading the crypto object? Able to encrypt/decrypt using standalone programme but facing issues with apigee java callout..

Sample java code:

public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
  JCEMapper.registerDefaultAlgorithms();
  org.apache.xml.security.Init.init();
  try {
      WSSecurityEngine newEngine = new WSSecurityEngine();   
      if (secEngine == null) newEngine = new WSSecurityEngine(); 
      if (crypto == null) crypto = CryptoFactory.getInstance();
      messageContext.setVariable("cl", Loader.getClassLoader(CryptoFactory.class));

Error..

org.apache.wss4j.common.ext.WSSecurityException: Cannot create Crypto class org.apache.wss4j.common.crypto.Merlin Original Exception was org.apache.wss4j.common.ext.WSSecurityException: No message with ID "failedCredentialLoad" found in resource bundle "org/apache/xml/security/resource/xmlsecurity". Original Exception was a java.io.EOFException and message null Original Exception was java.io.EOFException at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:122) at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:187) at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:179) at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:55) at com.xxxpki.Decrypt.execute(Decrypt.java:70) at com.apigee.steps.javacallout.JavaCalloutStepDefinition$ClassLoadWrappedExecution.execute(JavaCalloutStepDefinition.java:176) at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:242) at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:240) at java.security.AccessController.doPrivileged(Native Method) at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution.execute(JavaCalloutStepDefinition.java:240) at com.apigee.steps.javacallout.JavaCalloutStepDefinition$CallOutWrapper.execute(JavaCalloutStepDefinition.java:111) at com.apigee.messaging.runtime.steps.StepExecution.execute(StepExecution.java:146) at com.apigee.flow.execution.AbstractAsyncExecutionStrategy$AsyncExecutionTask.call(AbstractAsyncExecutionStrategy.java:74) at com.apigee.flow.execution.AbstractAsyncExecutionStrategy$AsyncExecutionTask.call(AbstractAsyncExecutionStrategy.java:45) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.apache.wss4j.common.ext.WSSecurityException: No message with ID "failedCredentialLoad" found in resource bundle "org/apache/xml/security/resource/xmlsecurity". Original Exception was a java.io.EOFException and message null Original Exception was java.io.EOFException at org.apache.wss4j.common.crypto.Merlin.load(Merlin.java:374) at org.apache.wss4j.common.crypto.Merlin.loadProperties(Merlin.java:228) at org.apache.wss4j.common.crypto.Merlin.<init>(Merlin.java:156) at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:119) ... 19 more Caused by: java.io.EOFException at java.io.DataInputStream.readInt(DataInputStream.java:392) at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:653) at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:56) at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:224) at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70) at java.security.KeyStore.load(KeyStore.java:1445) at org.apache.wss4j.common.crypto.Merlin.load(Merlin.java:370) ... 22 more 

==

Please suggest.

Solved Solved
0 5 5,134
1 ACCEPTED SOLUTION

yes, maybe you have your Jar set up incorrectly .

Here is code for an example Java callout that successfully initializes wss4j.

The important parts of this code:

  • the crypto.properties file is embedded as a resource into the JAR
  • the org.apache.ws.security.crypto.merlin.keystore.file= setting in that file must point to another resource embedded into the JAR.
  • I had to conditionally initialize the BouncyCastle security provider. When running in a test mode (outside the message processor) it's required to add BC as a provider. When running in the MP, it is forbidden to add BC as a provider. So wrap it in a condition. My code looks like this:
    static {
        // Initialize BouncyCastle conditionally. Security.addProvider() is not permitted when run within Apigee Edge,
        // and is not necessary there, anyway, since Edge includes it. addProvider() is required when running outside
        // of Edge, as with running unit tests, or in a standalone client.
        if (java.security.Security.getProvider("BC") == null)
            java.security.Security.addProvider(new BouncyCastleProvider());
    }
  • I also found that he order of initialization of the various security things was important. My code does this:
    public Signature() throws Exception {
        WSSConfig.setAddJceProviders(false);
        org.apache.wss4j.stax.setup.WSSec.init();
        org.apache.xml.security.Init.init();
        WSSConfig.init();
    }

I don't remember exactly what the problem was, but I do remember that if I did not include this particular combination in this order, then the callout class failed to initialize. I learned this through unit testing, by the way! Do you have unit tests on your callout? It's a good idea!

good luck!

View solution in original post

5 REPLIES 5

@Dino @DinoChiesa-at-Google

Kindly suggest as it is so frustrating to run in apigee and spending time triaging it 😞

Case # 1440218 opened for assistance as well which has proxy uploaded. Any one from engineering team kindly suggest.

Further debug shows as below.

09:20:36.466 [Apigee-Main-8] DEBUG org.apache.wss4j.common.util.Loader - Trying to find [keys/pki.jks] using com.apigee.messaging.resource.JavaResourceClassLoader@761cb9b4 class loader.
09:20:36.467 [Apigee-Main-8] WARN c.a.m.r.JavaResourceClassLoader - Non Apigee class loaded from gateway's ClassLoader : sun.security.provider.JavaKeyStore
09:20:36.468 [Apigee-Main-8] WARN c.a.m.r.JavaResourceClassLoader - Non Apigee class loaded from gateway's ClassLoader : sun.security.provider.JavaKeyStore$JKS
09:20:36.468 [Apigee-Main-8] WARN c.a.m.r.JavaResourceClassLoader - Non Apigee class loaded from gateway's ClassLoader : sun.security.provider.KeyStoreDelegator
09:20:36.468 [Apigee-Main-8] WARN c.a.m.r.JavaResourceClassLoader - Non Apigee class loaded from gateway's ClassLoader : sun.security.provider.JavaKeyStore$DualFormatJKS
09:20:36.469 [Apigee-Main-8] DEBUG o.apache.wss4j.common.crypto.Merlin - null
java.io.EOFException: null
at java.io.DataInputStream.readInt(DataInputStream.java:392) ~[na:1.8.0_161]
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:653) ~[na:1.8.0_161]
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:56) ~[na:1.8.0_161]
at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:224) ~[na:1.8.0_161]
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70) ~[na:1.8.0_161]
at java.security.KeyStore.load(KeyStore.java:1445) ~[na:1.8.0_161]
at org.apache.wss4j.common.crypto.Merlin.load(Merlin.java:370) [pkinew.jar/:na]
at org.apache.wss4j.common.crypto.Merlin.loadProperties(Merlin.java:228) [pkinew.jar/:na]
at org.apache.wss4j.common.crypto.Merlin.<init>(Merlin.java:156) [pkinew.jar/:na]
at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:119) [pkinew.jar/:na]
at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:187) [pkinew.jar/:na]
at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:179) [pkinew.jar/:na]
at org.apache.wss4j.common.crypto.CryptoFactory.getInstance(CryptoFactory.java:55) [pkinew.jar/:na]
at com.xxxpki.Decrypt.execute(Decrypt.java:70) [pkinew.jar/:na]
at com.apigee.steps.javacallout.JavaCalloutStepDefinition$ClassLoadWrappedExecution.execute(JavaCalloutStepDefinition.java:176) [javacallout-1.0.0.jar:na]
at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:242) [javacallout-1.0.0.jar:na]
at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution$1.run(JavaCalloutStepDefinition.java:240) [javacallout-1.0.0.jar:na]
at java.security.AccessController.doPrivileged(Native Method) [na:1.8.0_161]
at com.apigee.steps.javacallout.JavaCalloutStepDefinition$SecurityWrappedExecution.execute(JavaCalloutStepDefinition.java:240) [javacallout-1.0.0.jar:na]
at com.apigee.steps.javacallout.JavaCalloutStepDefinition$CallOutWrapper.execute(JavaCalloutStepDefinition.java:111) [javacallout-1.0.0.jar:na]
at com.apigee.messaging.runtime.steps.StepExecution.execute(StepExecution.java:146) [message-processor-1.0.0.jar:na]

yes, maybe you have your Jar set up incorrectly .

Here is code for an example Java callout that successfully initializes wss4j.

The important parts of this code:

  • the crypto.properties file is embedded as a resource into the JAR
  • the org.apache.ws.security.crypto.merlin.keystore.file= setting in that file must point to another resource embedded into the JAR.
  • I had to conditionally initialize the BouncyCastle security provider. When running in a test mode (outside the message processor) it's required to add BC as a provider. When running in the MP, it is forbidden to add BC as a provider. So wrap it in a condition. My code looks like this:
    static {
        // Initialize BouncyCastle conditionally. Security.addProvider() is not permitted when run within Apigee Edge,
        // and is not necessary there, anyway, since Edge includes it. addProvider() is required when running outside
        // of Edge, as with running unit tests, or in a standalone client.
        if (java.security.Security.getProvider("BC") == null)
            java.security.Security.addProvider(new BouncyCastleProvider());
    }
  • I also found that he order of initialization of the various security things was important. My code does this:
    public Signature() throws Exception {
        WSSConfig.setAddJceProviders(false);
        org.apache.wss4j.stax.setup.WSSec.init();
        org.apache.xml.security.Init.init();
        WSSConfig.init();
    }

I don't remember exactly what the problem was, but I do remember that if I did not include this particular combination in this order, then the callout class failed to initialize. I learned this through unit testing, by the way! Do you have unit tests on your callout? It's a good idea!

good luck!

Trying to convert but still no success yet. Code works standalone but doesn't gets ported to apigee world. We are using

org.apache.xml.security.encryption.XMLCipher as apposed to wss4j and now we have to redo..Trying to see if there is something which you already done and have a generic working code else we will further spend hours to have it done..

Related question as posted below..

https://community.apigee.com/questions/55142/decrypt-an-xml-encrypted-message.html

Update:

As of 2019, Apigee has restricted which Java classes can run in Java callouts. WSS4J will not work, which means that older callout won't work.

You need to use this different WS-Security callout

https://github.com/DinoChiesa/ApigeeEdge-Java-WsSec-Signature-2