Wednesday, February 5, 2014

Apache WSS4J 2.0.0 - part IV

This is the fourth of a series of articles on the new features and changes that will be delivered in Apache WSS4J 2.0.0. The third article looked at some changes in the area of caching tokens to detect replay attacks. This post looks at a new feature of WSS4J 2.0.0, which is the ability to encrypt passwords in Crypto properties files.

1) Crypto properties

Apache WSS4J uses the Crypto interface to get keys and certificates for encryption/decryption and for signature creation/verification. WSS4J currently ships with three implementations:
  • Merlin: The standard implementation, based around two JDK keystores for key/cert retrieval, and trust verification.
  • CertificateStore: Holds an array of X509 Certificates. Can only be used for encryption and signature verification.
  • MerlinDevice: Based on Merlin, allows loading of keystores using a null InputStream - for example on a smart-card device.
Typically, a Crypto implementation is loaded and configured via a Crypto properties file. This tells WSS4J what Crypto implementation to load, as well as implementation-specific properties such as a keystore location, password, default alias to use, etc. A typical example of the contents of a Crypto properties file for Signature creation is as follows:

org.apache.wss4j.crypto.provider=org.apache.wss4j.common.crypto.Merlin
org.apache.wss4j.crypto.merlin.keystore.type=jks
org.apache.wss4j.crypto.merlin.keystore.password=security
org.apache.wss4j.crypto.merlin.keystore.alias=wss40
org.apache.wss4j.crypto.merlin.keystore.file=keys/wss40.jks

Note that in WSS4J 2.0.0 the "org.apache.ws.security.crypto" prefix used previously is now "org.apache.wss4j.crypto", however both prefixes are accepted by the code.

2) Encrypting passwords in a Crypto properties file

Note in the example above that the password used to load the keystore is in cleartext. One of the new features of Apache WSS4J 2.0.0 is the ability to instead store a (BASE-64 encoded) encrypted version of the keystore password in the Crypto properties file. A new PasswordEncryptor interface is defined to allow for the encryption/decryption of passwords. A default implementation is now provided based on Jasypt called JasyptPasswordEncryptor, which uses "PBEWithMD5AndTripleDES".

The way this works is as follows. The WSPasswordCallback class has an additional "usage" called WSPasswordCallback.PASSWORD_ENCRYPTOR_PASSWORD, which is used to return the password used with a PasswordEncryptor implementation to decrypt encrypted passwords stored in Crypto properties files. When WSS4J is loading a Crypto implementation via a properties file, and it encounters a password encrypted in the format "ENC(encoded encrypted password)", it queries a CallbackHandler for the (master) password to decrypt the encrypted password
using the WSPasswordCallback usage tag given above.

It is possible to pass a custom PasswordEncryptor implementation to WSS4J via the new configuration tag ConfigurationConstants.PASSWORD_ENCRYPTOR_INSTANCE ("passwordEncryptorInstance").

5 comments:

  1. Thanks for the insights on WSS4J!
    At the time being we use WSS4J embedded in CXF and we load the trustsotre and keystore from jks files using merlin.
    Is there a way to configure WSS4J and CXF to read certs and PK from a database?
    Thanks

    ReplyDelete
  2. Hi Matteo,

    There is no "out-of-the-box" way to do this. However, it is quite straightforward to implement the Crypto interface yourself and plug this into WSS4J/CXF. For example, there is an implementation in CXF that gets certs from an XKMS server:

    http://svn.apache.org/viewvc/cxf/trunk/services/xkms/xkms-client/src/main/java/org/apache/cxf/xkms/crypto/provider/XkmsCryptoProvider.java?view=markup

    Colm.

    ReplyDelete
  3. Oh! And if I understood correctly, CXF provides also a XKMS server. I did't know that.
    Many thanks

    ReplyDelete
  4. From the org.apache.cxf.ws.security.wss4j.SamlTokenInterceptor, it seems always get a Crypto with null PasswordEncryptor. Therefore, the Jasypt encrypted password is not properly decypted. Any advise to bypass this issue?

    cxf version 3.1.2, with wss4j 2.1.2
    --------------------
    getInstance(properties, Loader.getClassLoader(CryptoFactory.class), null);

    --------------------------

    public static Crypto getInstance(Properties properties) throws WSSecurityException {
    if (properties == null) {
    if (LOG.isDebugEnabled()) {
    LOG.debug("Cannot load Crypto instance as properties object is null");
    }
    throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
    "empty", new Object[] {"Cannot load Crypto instance as properties object is null"});
    }
    return getInstance(properties, Loader.getClassLoader(CryptoFactory.class), null);
    }

    ReplyDelete