blob: 916fca963674719c1649cee80d2a77de8d1fe9a5 [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.stax.validate;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.apache.wss4j.binding.wss10.AttributedString;
import org.apache.wss4j.binding.wss10.EncodedString;
import org.apache.wss4j.binding.wss10.PasswordString;
import org.apache.wss4j.binding.wss10.UsernameTokenType;
import org.apache.wss4j.binding.wsu10.AttributedDateTime;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.UsernameTokenUtil;
import org.apache.wss4j.stax.ext.WSSConstants;
import org.apache.wss4j.stax.securityToken.UsernameSecurityToken;
import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
import org.apache.wss4j.stax.utils.WSSUtils;
import org.apache.wss4j.stax.impl.securityToken.UsernameSecurityTokenImpl;
import org.apache.xml.security.stax.ext.XMLSecurityUtils;
import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
import org.apache.xml.security.utils.XMLUtils;
public class UsernameTokenValidatorImpl implements UsernameTokenValidator {
private static final transient org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(UsernameTokenValidatorImpl.class);
@Override
public <T extends UsernameSecurityToken & InboundSecurityToken> T validate(
UsernameTokenType usernameTokenType, TokenContext tokenContext) throws WSSecurityException {
// If the UsernameToken is to be used for key derivation, the (1.1)
// spec says that it cannot contain a password, and it must contain
// an Iteration element
final byte[] salt = XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE11_SALT);
PasswordString passwordType = XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_PASSWORD);
final Long iteration = XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE11_ITERATION);
if (salt != null && (passwordType != null || iteration == null)) {
throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "badTokenType01");
}
boolean handleCustomPasswordTypes = tokenContext.getWssSecurityProperties().getHandleCustomPasswordTypes();
boolean allowUsernameTokenNoPassword =
tokenContext.getWssSecurityProperties().isAllowUsernameTokenNoPassword()
|| Boolean.parseBoolean((String)tokenContext.getWsSecurityContext().get(WSSConstants.PROP_ALLOW_USERNAMETOKEN_NOPASSWORD));
// Check received password type against required type
WSSConstants.UsernameTokenPasswordType requiredPasswordType =
tokenContext.getWssSecurityProperties().getUsernameTokenPasswordType();
if (requiredPasswordType != null) {
if (passwordType == null || passwordType.getType() == null) {
LOG.warn("Authentication failed as the received password type does not "
+ "match the required password type of: {}", requiredPasswordType);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
WSSConstants.UsernameTokenPasswordType usernameTokenPasswordType =
WSSConstants.UsernameTokenPasswordType.getUsernameTokenPasswordType(passwordType.getType());
if (requiredPasswordType != usernameTokenPasswordType) {
LOG.warn("Authentication failed as the received password type does not "
+ "match the required password type of: {}", requiredPasswordType);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
}
WSSConstants.UsernameTokenPasswordType usernameTokenPasswordType = WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE;
if (passwordType != null && passwordType.getType() != null) {
usernameTokenPasswordType = WSSConstants.UsernameTokenPasswordType.getUsernameTokenPasswordType(passwordType.getType());
}
final AttributedString username = usernameTokenType.getUsername();
if (username == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "badTokenType01");
}
final EncodedString encodedNonce =
XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSSE_NONCE);
byte[] nonceVal = null;
if (encodedNonce != null && encodedNonce.getValue() != null) {
nonceVal = XMLUtils.decode(encodedNonce.getValue());
}
final AttributedDateTime attributedDateTimeCreated =
XMLSecurityUtils.getQNameType(usernameTokenType.getAny(), WSSConstants.TAG_WSU_CREATED);
String created = null;
if (attributedDateTimeCreated != null) {
created = attributedDateTimeCreated.getValue();
}
if (usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST) {
if (encodedNonce == null || attributedDateTimeCreated == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "badTokenType01");
}
if (!WSSConstants.SOAPMESSAGE_NS10_BASE64_ENCODING.equals(encodedNonce.getEncodingType())) {
throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN, "badTokenType01");
}
verifyDigestPassword(username.getValue(), passwordType, nonceVal, created, tokenContext);
} else if (usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_TEXT
|| passwordType != null && passwordType.getValue() != null
&& usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE) {
verifyPlaintextPassword(username.getValue(), passwordType, tokenContext);
} else if (passwordType != null && passwordType.getValue() != null) {
if (!handleCustomPasswordTypes) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
verifyCustomPassword(username.getValue(), passwordType, tokenContext);
} else {
if (!allowUsernameTokenNoPassword) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
}
final String password;
if (passwordType != null) {
password = passwordType.getValue();
} else if (salt != null) {
WSPasswordCallback pwCb = new WSPasswordCallback(username.getValue(),
WSPasswordCallback.USERNAME_TOKEN);
try {
WSSUtils.doPasswordCallback(tokenContext.getWssSecurityProperties().getCallbackHandler(), pwCb);
} catch (WSSecurityException e) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e);
}
password = pwCb.getPassword();
} else {
password = null;
}
UsernameSecurityTokenImpl usernameSecurityToken = new UsernameSecurityTokenImpl(
usernameTokenPasswordType, username.getValue(), password, created,
nonceVal, salt, iteration,
tokenContext.getWsSecurityContext(), usernameTokenType.getId(),
WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE);
usernameSecurityToken.setElementPath(tokenContext.getElementPath());
usernameSecurityToken.setXMLSecEvent(tokenContext.getFirstXMLSecEvent());
@SuppressWarnings("unchecked")
T token = (T)usernameSecurityToken;
return token;
}
/**
* Verify a UsernameToken containing a password digest.
*/
protected void verifyDigestPassword(
String username,
PasswordString passwordType,
byte[] nonceVal,
String created,
TokenContext tokenContext
) throws WSSecurityException {
WSPasswordCallback pwCb = new WSPasswordCallback(username,
null,
passwordType.getType(),
WSPasswordCallback.USERNAME_TOKEN);
try {
WSSUtils.doPasswordCallback(tokenContext.getWssSecurityProperties().getCallbackHandler(), pwCb);
} catch (WSSecurityException e) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e);
}
if (pwCb.getPassword() == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
byte[] passDigest = UsernameTokenUtil.doRawPasswordDigest(nonceVal, created,
pwCb.getPassword().getBytes(StandardCharsets.UTF_8));
byte[] decodedPassword = XMLUtils.decode(passwordType.getValue());
if (!MessageDigest.isEqual(decodedPassword, passDigest)) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
passwordType.setValue(pwCb.getPassword());
}
/**
* Verify a UsernameToken containing a plaintext password.
*/
protected void verifyPlaintextPassword(
String username,
PasswordString passwordType,
TokenContext tokenContext
) throws WSSecurityException {
WSPasswordCallback pwCb = new WSPasswordCallback(username,
null,
passwordType.getType(),
WSPasswordCallback.USERNAME_TOKEN);
try {
WSSUtils.doPasswordCallback(tokenContext.getWssSecurityProperties().getCallbackHandler(), pwCb);
} catch (WSSecurityException e) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e);
}
if (pwCb.getPassword() == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
byte[] origPasswordBytes = pwCb.getPassword().getBytes(StandardCharsets.UTF_8);
byte[] passwordBytes = passwordType.getValue().getBytes(StandardCharsets.UTF_8);
if (!MessageDigest.isEqual(origPasswordBytes, passwordBytes)) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
}
passwordType.setValue(pwCb.getPassword());
}
/**
* Verify a UsernameToken containing a password of some unknown (but specified) password
* type.
*/
protected void verifyCustomPassword(
String username,
PasswordString passwordType,
TokenContext tokenContext
) throws WSSecurityException {
verifyPlaintextPassword(username, passwordType, tokenContext);
}
}