/**
* 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.impl.processor.input;
import java.security.Key;
import java.util.Deque;
import java.util.List;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.apache.wss4j.binding.wssc.AbstractDerivedKeyTokenType;
import org.apache.wss4j.common.derivedKey.DerivedKeyUtils;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
import org.apache.wss4j.stax.ext.WSSConstants;
import org.apache.wss4j.stax.ext.WSSSecurityProperties;
import org.apache.wss4j.stax.impl.securityToken.SecurityTokenFactoryImpl;
import org.apache.wss4j.stax.securityEvent.DerivedKeyTokenSecurityEvent;
import org.apache.wss4j.stax.securityToken.UsernameSecurityToken;
import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
import org.apache.xml.security.stax.ext.AbstractInputSecurityHeaderHandler;
import org.apache.xml.security.stax.ext.InputProcessorChain;
import org.apache.xml.security.stax.ext.XMLSecurityConstants;
import org.apache.xml.security.stax.ext.XMLSecurityProperties;
import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
import org.apache.xml.security.stax.impl.securityToken.AbstractInboundSecurityToken;
import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.apache.xml.security.stax.securityEvent.AlgorithmSuiteSecurityEvent;
import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
/**
* Processor for the SecurityContextToken XML Structure
*/
public class DerivedKeyTokenInputHandler extends AbstractInputSecurityHeaderHandler {
@Override
public void handle(final InputProcessorChain inputProcessorChain, final XMLSecurityProperties securityProperties,
Deque<XMLSecEvent> eventQueue, Integer index) throws XMLSecurityException {
@SuppressWarnings("unchecked")
final AbstractDerivedKeyTokenType derivedKeyTokenType =
((JAXBElement<AbstractDerivedKeyTokenType>) parseStructure(eventQueue, index, securityProperties)).getValue();
if (derivedKeyTokenType.getId() == null) {
derivedKeyTokenType.setId(IDGenerator.generateID(null));
}
if (derivedKeyTokenType.getSecurityTokenReference() == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, "noReference");
}
final List<QName> elementPath = getElementPath(eventQueue);
final XMLSecEvent responsibleXMLSecStartXMLEvent = getResponsibleStartXMLEvent(eventQueue, index);
SecurityTokenProvider<InboundSecurityToken> securityTokenProvider = new SecurityTokenProvider<InboundSecurityToken>() {
private AbstractInboundSecurityToken derivedKeySecurityToken;
@Override
public InboundSecurityToken getSecurityToken() throws XMLSecurityException {
if (this.derivedKeySecurityToken != null) {
return this.derivedKeySecurityToken;
}
//todo implement interface to access all derivedKeys? The same would be needed in UserNameToken
this.derivedKeySecurityToken = new AbstractInboundSecurityToken(
(WSInboundSecurityContext) inputProcessorChain.getSecurityContext(),
derivedKeyTokenType.getId(), WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE,
true) {
private InboundSecurityToken referencedSecurityToken;
private InboundSecurityToken getReferencedSecurityToken() throws XMLSecurityException {
if (this.referencedSecurityToken != null) {
return referencedSecurityToken;
}
this.referencedSecurityToken = SecurityTokenFactoryImpl.getSecurityToken(
derivedKeyTokenType.getSecurityTokenReference(),
((WSSSecurityProperties) securityProperties).getDecryptionCrypto(),
((WSSSecurityProperties)securityProperties).getCallbackHandler(),
inputProcessorChain.getSecurityContext(),
((WSSSecurityProperties)securityProperties)
);
this.referencedSecurityToken.addWrappedToken(this);
return this.referencedSecurityToken;
}
@Override
protected Key getKey(String algorithmURI, XMLSecurityConstants.AlgorithmUsage algorithmUsage,
String correlationID) throws XMLSecurityException {
byte[] secret;
InboundSecurityToken referencedSecurityToken = getReferencedSecurityToken();
if (referencedSecurityToken != null) {
if (referencedSecurityToken instanceof UsernameSecurityToken) {
UsernameSecurityToken usernameSecurityToken = (UsernameSecurityToken) referencedSecurityToken;
secret = usernameSecurityToken.generateDerivedKey();
} else {
secret = referencedSecurityToken.getSecretKey(algorithmURI, algorithmUsage, correlationID).getEncoded();
}
} else {
throw new WSSecurityException(WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, "unsupportedKeyId");
}
byte[] nonce = derivedKeyTokenType.getNonce();
if (nonce == null || nonce.length == 0) {
throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "empty",
new Object[] {"Missing wsc:Nonce value"});
}
String derivedKeyAlgorithm = derivedKeyTokenType.getAlgorithm();
if (derivedKeyAlgorithm == null) {
derivedKeyAlgorithm = WSSConstants.P_SHA_1;
}
byte[] keyBytes = DerivedKeyUtils.deriveKey(
derivedKeyAlgorithm,
derivedKeyTokenType.getLabel(),
derivedKeyTokenType.getLength().intValue(),
secret,
nonce,
derivedKeyTokenType.getOffset().intValue()
);
XMLSecurityConstants.AlgorithmUsage derivedKeyAlgorithmUsage;
if (WSSConstants.Enc.equals(algorithmUsage)) {
derivedKeyAlgorithmUsage = WSSConstants.ENC_KD;
} else {
derivedKeyAlgorithmUsage = WSSConstants.SIG_KD;
}
AlgorithmSuiteSecurityEvent algorithmSuiteSecurityEvent = new AlgorithmSuiteSecurityEvent();
algorithmSuiteSecurityEvent.setAlgorithmURI(derivedKeyAlgorithm);
algorithmSuiteSecurityEvent.setAlgorithmUsage(derivedKeyAlgorithmUsage);
algorithmSuiteSecurityEvent.setKeyLength(keyBytes.length * 8);
algorithmSuiteSecurityEvent.setCorrelationID(correlationID);
inputProcessorChain.getSecurityContext().registerSecurityEvent(algorithmSuiteSecurityEvent);
String algo = JCEAlgorithmMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
return new SecretKeySpec(keyBytes, algo);
}
@Override
public InboundSecurityToken getKeyWrappingToken() throws XMLSecurityException {
return getReferencedSecurityToken();
}
@Override
public WSSecurityTokenConstants.TokenType getTokenType() {
return WSSecurityTokenConstants.DerivedKeyToken;
}
};
this.derivedKeySecurityToken.setElementPath(elementPath);
this.derivedKeySecurityToken.setXMLSecEvent(responsibleXMLSecStartXMLEvent);
return this.derivedKeySecurityToken;
}
@Override
public String getId() {
return derivedKeyTokenType.getId();
}
};
inputProcessorChain.getSecurityContext().registerSecurityTokenProvider(derivedKeyTokenType.getId(), securityTokenProvider);
//fire a tokenSecurityEvent
DerivedKeyTokenSecurityEvent derivedKeyTokenSecurityEvent = new DerivedKeyTokenSecurityEvent();
derivedKeyTokenSecurityEvent.setSecurityToken(securityTokenProvider.getSecurityToken());
derivedKeyTokenSecurityEvent.setCorrelationID(derivedKeyTokenType.getId());
inputProcessorChain.getSecurityContext().registerSecurityEvent(derivedKeyTokenSecurityEvent);
}
}