View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.wss4j.stax.impl.processor.output;
20  
21  import org.apache.wss4j.common.ext.WSPasswordCallback;
22  import org.apache.wss4j.common.ext.WSSecurityException;
23  import org.apache.wss4j.common.util.DateUtil;
24  import org.apache.wss4j.common.util.UsernameTokenUtil;
25  import org.apache.wss4j.stax.ext.WSSConstants;
26  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
27  import org.apache.wss4j.stax.impl.securityToken.OutboundUsernameSecurityToken;
28  import org.apache.wss4j.stax.utils.WSSUtils;
29  import org.apache.xml.security.exceptions.XMLSecurityException;
30  import org.apache.xml.security.stax.ext.*;
31  import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
32  import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
33  import org.apache.xml.security.stax.impl.util.IDGenerator;
34  import org.apache.xml.security.stax.securityToken.OutboundSecurityToken;
35  import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
36  import org.apache.xml.security.utils.XMLUtils;
37  
38  import javax.security.auth.callback.CallbackHandler;
39  import javax.xml.namespace.QName;
40  import javax.xml.stream.XMLStreamException;
41  
42  import java.time.Instant;
43  import java.time.ZoneOffset;
44  import java.util.ArrayList;
45  import java.util.List;
46  
47  public class UsernameTokenOutputProcessor extends AbstractOutputProcessor {
48  
49      public UsernameTokenOutputProcessor() throws XMLSecurityException {
50          super();
51          addBeforeProcessor(WSSSignatureOutputProcessor.class);
52          addBeforeProcessor(EncryptOutputProcessor.class);
53      }
54  
55      @Override
56      public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
57          throws XMLStreamException, XMLSecurityException {
58  
59          try {
60              CallbackHandler callbackHandler = ((WSSSecurityProperties)getSecurityProperties()).getCallbackHandler();
61              WSSConstants.UsernameTokenPasswordType usernameTokenPasswordType =
62                  ((WSSSecurityProperties) getSecurityProperties()).getUsernameTokenPasswordType();
63  
64              if (callbackHandler == null
65                  && WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE != usernameTokenPasswordType) {
66                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noCallback");
67              }
68  
69              String password = null;
70              if (callbackHandler != null) {
71                  WSPasswordCallback pwCb =
72                      new WSPasswordCallback(((WSSSecurityProperties) getSecurityProperties()).getTokenUser(),
73                                             WSPasswordCallback.USERNAME_TOKEN);
74                  WSSUtils.doPasswordCallback(callbackHandler, pwCb);
75                  password = pwCb.getPassword();
76              }
77  
78              if (password == null && WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE != usernameTokenPasswordType) {
79                  final Object[] args = {((WSSSecurityProperties) getSecurityProperties()).getTokenUser() };
80                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noPassword", args);
81              }
82  
83              final String wsuId = IDGenerator.generateID(null);
84  
85              boolean useDerivedKeyForMAC =
86                  ((WSSSecurityProperties)getSecurityProperties()).isUseDerivedKeyForMAC();
87              int derivedIterations =
88                  ((WSSSecurityProperties)getSecurityProperties()).getDerivedKeyIterations();
89              byte[] salt = null;
90              if (WSSConstants.USERNAMETOKEN_SIGNED.equals(getAction())) {
91                  salt = UsernameTokenUtil.generateSalt(useDerivedKeyForMAC);
92              }
93  
94              byte[] nonceValue = null;
95              if (usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST
96                  || ((WSSSecurityProperties) getSecurityProperties()).isAddUsernameTokenNonce()) {
97                  nonceValue = WSSConstants.generateBytes(16);
98              }
99  
100             String createdStr = "";
101             if (usernameTokenPasswordType == WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST
102                 || ((WSSSecurityProperties) getSecurityProperties()).isAddUsernameTokenCreated()) {
103                 Instant created = Instant.now();
104                 createdStr = created.atZone(ZoneOffset.UTC).format(DateUtil.getDateTimeFormatter(true));
105             }
106 
107             final OutputProcessor outputProcessor = this;
108 
109             final OutboundUsernameSecurityToken usernameSecurityToken =
110                     new OutboundUsernameSecurityToken(((WSSSecurityProperties) getSecurityProperties()).getTokenUser(),
111                             password,
112                             createdStr,
113                             nonceValue,
114                             wsuId,
115                             salt,
116                             derivedIterations
117                     );
118             usernameSecurityToken.setProcessor(outputProcessor);
119 
120             SecurityTokenProvider<OutboundSecurityToken> securityTokenProvider =
121                     new SecurityTokenProvider<OutboundSecurityToken>() {
122 
123                 @Override
124                 public OutboundSecurityToken getSecurityToken() throws WSSecurityException {
125                     return usernameSecurityToken;
126                 }
127 
128                 @Override
129                 public String getId() {
130                     return wsuId;
131                 }
132             };
133             if (WSSConstants.USERNAMETOKEN_SIGNED.equals(getAction())) {
134                 outputProcessorChain.getSecurityContext().registerSecurityTokenProvider(wsuId, securityTokenProvider);
135                 outputProcessorChain.getSecurityContext().put(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_SIGNATURE, wsuId);
136             }
137             final FinalUsernameTokenOutputProcessor finalUsernameTokenOutputProcessor =
138                 new FinalUsernameTokenOutputProcessor(wsuId, nonceValue, password, createdStr, salt, derivedIterations, getAction());
139             getBeforeProcessors().forEach(finalUsernameTokenOutputProcessor::addBeforeProcessor);
140             finalUsernameTokenOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
141             finalUsernameTokenOutputProcessor.setAction(getAction(), getActionOrder());
142             finalUsernameTokenOutputProcessor.init(outputProcessorChain);
143 
144         } finally {
145             outputProcessorChain.removeProcessor(this);
146         }
147         outputProcessorChain.processEvent(xmlSecEvent);
148     }
149 
150     static class FinalUsernameTokenOutputProcessor extends AbstractOutputProcessor {
151 
152         private String wsuId;
153         private byte[] nonceValue;
154         private String password;
155         private String created;
156         private byte[] salt;
157         private int iterations;
158         private XMLSecurityConstants.Action action;
159 
160         FinalUsernameTokenOutputProcessor(String wsuId, byte[] nonceValue, String password,
161                                           String created, byte[] salt,
162                                           int iterations, XMLSecurityConstants.Action action)
163                 throws XMLSecurityException {
164             super();
165             this.addAfterProcessor(UsernameTokenOutputProcessor.class);
166             this.wsuId = wsuId;
167             this.nonceValue = nonceValue;
168             this.password = password;
169             this.created = created;
170             this.salt = salt;
171             this.iterations = iterations;
172             this.action = action;
173         }
174 
175         @Override
176         public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
177                 throws XMLStreamException, XMLSecurityException {
178 
179             outputProcessorChain.processEvent(xmlSecEvent);
180 
181             if (WSSUtils.isSecurityHeaderElement(xmlSecEvent, ((WSSSecurityProperties) getSecurityProperties()).getActor())) {
182 
183                 final QName headerElementName = WSSConstants.TAG_WSSE_USERNAME_TOKEN;
184                 OutputProcessorUtils.updateSecurityHeaderOrder(outputProcessorChain, headerElementName, getAction(), false);
185 
186                 OutputProcessorChain subOutputProcessorChain = outputProcessorChain.createSubChain(this);
187 
188                 List<XMLSecAttribute> attributes = new ArrayList<>(1);
189                 attributes.add(createAttribute(WSSConstants.ATT_WSU_ID, this.wsuId));
190                 createStartElementAndOutputAsEvent(subOutputProcessorChain, headerElementName, false, attributes);
191                 createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_USERNAME, false, null);
192                 createCharactersAndOutputAsEvent(subOutputProcessorChain,
193                                                  ((WSSSecurityProperties) getSecurityProperties()).getTokenUser());
194                 createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_USERNAME);
195                 if (((WSSSecurityProperties) getSecurityProperties()).getUsernameTokenPasswordType()
196                     != WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE && !WSSConstants.USERNAMETOKEN_SIGNED.equals(action)) {
197                     attributes = new ArrayList<>(1);
198                     attributes.add(createAttribute(WSSConstants.ATT_NULL_Type,
199                             ((WSSSecurityProperties) getSecurityProperties()).getUsernameTokenPasswordType()
200                                 == WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST
201                                     ? WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST.getNamespace()
202                                     : WSSConstants.UsernameTokenPasswordType.PASSWORD_TEXT.getNamespace()));
203                     createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_PASSWORD, false, attributes);
204                     createCharactersAndOutputAsEvent(subOutputProcessorChain,
205                             ((WSSSecurityProperties) getSecurityProperties()).getUsernameTokenPasswordType()
206                                 == WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST
207                                     ? UsernameTokenUtil.doPasswordDigest(this.nonceValue, created, this.password)
208                                     : this.password);
209                     createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_PASSWORD);
210                 }
211 
212                 if (salt != null) {
213                     createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE11_SALT, true, null);
214                     createCharactersAndOutputAsEvent(subOutputProcessorChain, XMLUtils.encodeToString(this.salt));
215                     createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE11_SALT);
216 
217                     if (iterations > 0) {
218                         createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE11_ITERATION, true, null);
219                         createCharactersAndOutputAsEvent(subOutputProcessorChain, "" + iterations);
220                         createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE11_ITERATION);
221                     }
222                 }
223 
224                 if (nonceValue != null && !WSSConstants.USERNAMETOKEN_SIGNED.equals(action)) {
225                     attributes = new ArrayList<>(1);
226                     attributes.add(createAttribute(WSSConstants.ATT_NULL_ENCODING_TYPE, WSSConstants.SOAPMESSAGE_NS10_BASE64_ENCODING));
227                     createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_NONCE, false, attributes);
228                     createCharactersAndOutputAsEvent(subOutputProcessorChain,
229                                                     XMLUtils.encodeToString(this.nonceValue));
230                     createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_NONCE);
231                 }
232 
233                 if (created != null && created.length() != 0) {
234                     createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSU_CREATED, false, null);
235                     createCharactersAndOutputAsEvent(subOutputProcessorChain, created);
236                     createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSU_CREATED);
237                 }
238 
239                 createEndElementAndOutputAsEvent(subOutputProcessorChain, headerElementName);
240 
241                 outputProcessorChain.removeProcessor(this);
242             }
243         }
244     }
245 }