2

Greetings good people.

I have a soap web service that I want to consume.I have created a small project to simulate what is required in the actual project especially on the username token encryption.

There are steps provided on how to encrypt the password on the client side as listed below:

  • Write the unencrypted password value.
  • Then, encrypt the block of data created in step 1 with the public portion of the password key certificate. Use the RSA algorithm, and use PKCS #1.5 padding (not OAEP), and add the result to the encrypted stream – this becomes the encrypted password which is submitted via the API.
  • Convert the resulting encrypted byte array into a string using base64 encoding. Present this base64 encoded string in the API request as the initiator SecurityCredential value.
  • Password to be encrypted with a public key from an X509 certificate issued to the Initiator specifically for this purpose.

So far I have been able to create a client and the server and I'm able to send a request and get a response.

I'm also able to secure the web service by passing a username token with password as plain text in the ClientPasswordCallback class and checking these credentails in the ServerPasswordCallback class.

I have gone further and in a seperate request encrypted the body part of the message using wss4j, RSA, X509 whereby I have public key stored in the clientKey.jks and a private key stored in privateKey.jks and by providing appropriate passwords in client and server password call back handlers I have been able to encrypt the body part at the client and decrypt it at the server.

The Challenge: The main challenge I'm experiencing is combining the two steps above in a single request such that using the public key, I'm able to encrypt the password in the username token and decrypt the same at the server side using the private key.

NB I have generated the keys for testing using keygen tool that comes with the jdk.

I'm imagining that there will be two passwords in the ClientPasswordCallback class, one for the clientKey.jks keystore and the other other password that needs to be encrypted.

This is what I have been able to archive so far:

Client side

TestMathUtility class

public static void main(String[] args) {

    JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

    // Use the URL defined in the soap address portion of the WSDL
    factory.setAddress("http://localhost:8080/MathUtility/services/MathUtilityPort");

    // Utilize the class which was auto-generated by Apache CXF wsdl2java
    factory.setServiceClass(MathUtility.class);

    Object client = factory.create();

    // Adding Logging Interceptors
    LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
    loggingOutInterceptor.setPrettyLogging(true);
    ClientProxy.getClient(client).getOutInterceptors().add(loggingOutInterceptor);

    LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
    loggingInInterceptor.setPrettyLogging(true);
    ClientProxy.getClient(client).getInInterceptors().add(loggingInInterceptor);

    // Set up WS-Security Encryption
    // Reference: https://ws.apache.org/wss4j/using.html
    Map<String, Object> props = new HashMap<String, Object>();
    props.put(WSHandlerConstants.USER, "testkey");
    props.put(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
    props.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");
    props.put(WSHandlerConstants.ENC_PROP_FILE, "clientKeystore.properties");
    props.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");
    props.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());

    WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props);

    ClientProxy.getClient(client).getOutInterceptors().add(wss4jOut);

    try {

        // Call the Web Service to perform an operation
        int response = ((MathUtility)client).addIntegers(5, 10);

        System.out.println("Response we've got ========= "+response);

      } catch (SecurityException e) {
        e.printStackTrace();
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      }

}

ClientPasswordCallback class

public class ClientPasswordCallback implements CallbackHandler {

@Override
public void handle(Callback[] callbacks) throws IOException,
        UnsupportedCallbackException {

    WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

    // set the password for our message.
    pc.setPassword("clientstorepass");

}

}

Server side

MathUtility class

@WebService(targetNamespace = "http://utility.math.com/", portName =      "MathUtilityPort", serviceName = "MathUtilityService")
public class MathUtility {

public int addIntegers(int firstNum, int secondNum) {
    return firstNum + secondNum;
}

public int factorial(int n) {
    int result = 1;

    for (int i = 1; i <= n; i++) {
        result = result * i;
    }

    return result;
}
}

ServerPasswordCallback class

public class ServerPasswordCallback implements CallbackHandler {

@Override
public void handle(Callback[] arg0) throws IOException,
        UnsupportedCallbackException {

    WSPasswordCallback pc = (WSPasswordCallback) arg0[0];

    // set the password for our message.
    pc.setPassword("storepass");

}

}

cxf-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="myPasswordCallback" class="com.math.utility.security.ServerPasswordCallback"/>

<jaxws:endpoint xmlns:tns="http://utility.math.com/" id="mathutility"
    implementor="com.math.utility.MathUtility" wsdlLocation="wsdl/mathutility.wsdl"
    endpointName="tns:MathUtilityPort" serviceName="tns:MathUtilityService"
    address="/MathUtilityPort">
    <jaxws:features>
        <bean class="org.apache.cxf.feature.LoggingFeature" />
    </jaxws:features>

        <jaxws:inInterceptors>
          <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
             <constructor-arg>
                <map>
                   <entry key="user" value="testkey"/>
                   <entry key="action" value="Encrypt"/>
                   <entry key="passwordType" value="PasswordText"/>
                   <entry key="decryptionParts" value="{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
                   <entry key="decryptionPropFile" value="serverKeystore.properties"/>
                   <entry key="passwordCallbackRef">
                      <ref bean="myPasswordCallback"/>
                   </entry>
                </map>
             </constructor-arg>
          </bean>

       </jaxws:inInterceptors>

</jaxws:endpoint>

clientKeyStore.properties file the same structure is used on the server side

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=clientkeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=clientstorepass
org.apache.ws.security.crypto.merlin.keystore.type=jks

The .jks files used have not been provided

NB I'm not using spring.

chilopoda
  • 71
  • 2
  • 8

2 Answers2

0

If you want to have a custom digest you could override the method verifyCustomPassword(UsernameToken usernameToken, RequestData data) in UsernameTokenValidator

To hook it up to your webservice have a look at my answer to another SO-question. The essentials of this answers are:

<property name="wssConfig">
        <ref bean="usernameTokenWssConfig"/>
</property>

And add the referenced class to your codebase:

@Component("usernameTokenWssConfig")
public class UsernameTokenWssConfig extends WSSConfig {
    public UsernameTokenWssConfig() {
        setValidator(WSSecurityEngine.USERNAME_TOKEN, new CustomUsernameTokenValidator());
        setRequiredPasswordType(WSConstants.CUSTOM_TOKEN );
    }
}
Community
  • 1
  • 1
Frank
  • 2,036
  • 1
  • 20
  • 32
  • Hi Frank, Thanks for your time and input. I want to encrypt the password using a public key. I'm looking forward to doing something like this in my client code: `props.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");` – chilopoda Jun 03 '16 at 06:02
  • But instead of encrypting the Body, I should change that to UsernameToken so that only the usernametoken part is encrypted. Is that possible or I should encrypt the password programatically as you've suggested and then pass it in the header? – chilopoda Jun 03 '16 at 06:10
  • Hi, you could send your password as plaintext or as digest, that means oneway hashed. If you need to encrypt and decrypt the password than you should go with my suggested solution. – Frank Jun 03 '16 at 13:20
0

This is how I did the encryption Of UT using encryption parts

outProps.put(
    WSHandlerConstants.ENCRYPTION_PARTS,
    "{Content}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}UsernameToken"
);

In order to encrypt the username token with the encryption parts . Add the SOAP header namespace inside the second curly braces

I got an encrypted UT like below(I only encrypted the content only you can do the element if you want)

<wsse:UsernameToken wsu:Id="UsernameToken-99bea96d-c6ef-444c-aa8a-ec807f58aa0c">
  <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-59f84d2b-3195-436f-b8f4-513fea23c00a" Type="http://www.w3.org/2001/04/xmlenc#Content">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
        <wsse:Reference URI="#EK-748c3d27-f6be-4b81-b864-87bc6457e247" />
      </wsse:SecurityTokenReference>
    </ds:KeyInfo>
    <xenc:CipherData>
      <xenc:CipherValue>wSZsu9LR6q9fpUPYYF5GSA7T/3iZWMd0cB/80Z33DThzCB0kqnupGETVmGfVQheGUc3O+/B4X7i70aMTyOo5u0fIqa4kwrlKZBe9he359mpgakKgC4wOb65sDThT1fH4PvY6TSBjIOJ0T5jIyt1pGwacRLzmvFxxHxr3qfAOf27LLGJ0P0eAKchE19nAkfP+Tc2GbAkcxi/4SDQ7bBWVaveRgSET0dpheooBGORtt4VJ/dyMwogupAyJKoiqe3RFRCvsmK/UtkVGQYh/W14ei/s7G3mVAch8fQZXCS8jcEaqzkDaNzrZo8+IjJFgrPQY23g3fp57QXIDB84NNUhsm7NHXMNfAq7x97kng+Qwke6uqHcMPjGI9boKw/wZmhipYstFzUpOpF86W9FwcJPyTFR58jvdnX5OGJ1wFbFdI9cAjWdncIEmnOTl69pKRmGmbJYj7Ie43q+eNH/1+2RawBRhZG43VLZL5C7ydFu0xJ2DsD4nacvDfH0i8tcMCHyHkWf2po9Y/dBtS2kWAxfNxWQNvI1BceumsMvpSzK7WjXPJ/vaKlMoSQJtsBxg9RhA
      </xenc:CipherValue>
    </xenc:CipherData>
  </xenc:EncryptedData>
</wsse:UsernameToken>
Josef
  • 2,869
  • 2
  • 22
  • 23
jmonroe
  • 61
  • 2
  • 8