blob: 9988278e2f8d841cc1a38284428039dd56cca9c4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.wss4j.common.util;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.xml.security.stax.ext.XMLSecurityConstants;
public final class UsernameTokenUtil {
public static final int DEFAULT_ITERATION = 1000;
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(UsernameTokenUtil.class);
private UsernameTokenUtil() {
// complete
}
/**
* This static method generates a derived key as defined in WSS Username
* Token Profile.
*
* @param password The password to include in the key generation
* @param salt The Salt value
* @param iteration The Iteration value. If zero (0) is given the method uses the
* default value
* @return Returns the derived key a byte array
* @throws WSSecurityException
*/
public static byte[] generateDerivedKey(
byte[] password,
byte[] salt,
int iteration
) throws WSSecurityException {
byte[] pwSalt = new byte[salt.length + password.length];
System.arraycopy(password, 0, pwSalt, 0, password.length);
System.arraycopy(salt, 0, pwSalt, password.length, salt.length);
MessageDigest sha = null;
try {
sha = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException e) {
LOG.debug(e.getMessage(), e);
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, e, "decoding.general"
);
}
//
// Make the first hash round with start value
//
byte[] k = sha.digest(pwSalt);
//
// Perform the 1st up to iteration-1 hash rounds
//
int iter = iteration;
if (iter <= 0) {
iter = DEFAULT_ITERATION;
}
for (int i = 1; i < iter; i++) {
k = sha.digest(k);
}
return k;
}
/**
* This static method generates a derived key as defined in WSS Username
* Token Profile.
*
* @param password The password to include in the key generation
* @param salt The Salt value
* @param iteration The Iteration value. If zero (0) is given the method uses the
* default value
* @return Returns the derived key a byte array
* @throws WSSecurityException
*/
public static byte[] generateDerivedKey(
String password,
byte[] salt,
int iteration
) throws WSSecurityException {
return generateDerivedKey(password.getBytes(StandardCharsets.UTF_8), salt, iteration);
}
/**
* This static method generates a 128 bit salt value as defined in WSS
* Username Token Profile.
*
* @param useForMac If <code>true</code> define the Salt for use in a MAC
* @return Returns the 128 bit salt value as byte array
*/
public static byte[] generateSalt(boolean useForMac) {
byte[] saltValue = null;
try {
saltValue = generateNonce(16);
} catch (WSSecurityException ex) {
LOG.debug(ex.getMessage(), ex);
return null;
}
if (useForMac) {
saltValue[0] = 0x01;
} else {
saltValue[0] = 0x02;
}
return saltValue;
}
/**
* Generate a nonce of the given length using the SHA1PRNG algorithm. The SecureRandom
* instance that backs this method is cached for efficiency.
*
* @return a nonce of the given length
* @throws WSSecurityException
*/
private static byte[] generateNonce(int length) throws WSSecurityException {
try {
return XMLSecurityConstants.generateBytes(length);
} catch (Exception ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex,
"empty", new Object[] {"Error in generating nonce of length " + length}
);
}
}
public static String doPasswordDigest(byte[] nonce, String created, String password) throws WSSecurityException {
return doPasswordDigest(nonce, created, password.getBytes(StandardCharsets.UTF_8));
}
public static String doPasswordDigest(byte[] nonce, String created, byte[] password) throws WSSecurityException {
byte[] digestBytes = doRawPasswordDigest(nonce, created, password);
return org.apache.xml.security.utils.XMLUtils.encodeToString(digestBytes);
}
public static byte[] doRawPasswordDigest(byte[] nonce, String created, byte[] password) throws WSSecurityException {
try {
byte[] b1 = nonce != null ? nonce : new byte[0];
byte[] b2 = created != null ? created.getBytes(StandardCharsets.UTF_8) : new byte[0];
byte[] b3 = password;
byte[] b4 = new byte[b1.length + b2.length + b3.length];
int offset = 0;
System.arraycopy(b1, 0, b4, offset, b1.length);
offset += b1.length;
System.arraycopy(b2, 0, b4, offset, b2.length);
offset += b2.length;
System.arraycopy(b3, 0, b4, offset, b3.length);
return KeyUtils.generateDigest(b4);
} catch (Exception e) {
LOG.debug(e.getMessage(), e);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "decoding.general");
}
}
/**
* Get the raw (plain text) password used to compute secret key.
*/
public static String getRawPassword(CallbackHandler callbackHandler, String username,
String password, String passwordType) throws WSSecurityException {
if (callbackHandler == null) {
LOG.debug("CallbackHandler is null");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
WSPasswordCallback pwCb =
new WSPasswordCallback(
username, password, passwordType, WSPasswordCallback.USERNAME_TOKEN
);
try {
callbackHandler.handle(new Callback[]{pwCb});
} catch (IOException | UnsupportedCallbackException e) {
LOG.debug(e.getMessage(), e);
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e
);
}
return pwCb.getPassword();
}
}