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  
20  package org.apache.wss4j.dom.action;
21  
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  
26  import javax.security.auth.callback.CallbackHandler;
27  
28  import org.apache.wss4j.dom.WSConstants;
29  import org.apache.wss4j.common.SecurityActionToken;
30  import org.apache.wss4j.common.SignatureActionToken;
31  import org.apache.wss4j.common.WSEncryptionPart;
32  import org.apache.wss4j.common.ext.WSPasswordCallback;
33  import org.apache.wss4j.common.ext.WSSecurityException;
34  import org.apache.wss4j.common.util.UsernameTokenUtil;
35  import org.apache.wss4j.dom.handler.RequestData;
36  import org.apache.wss4j.dom.handler.WSHandler;
37  import org.apache.wss4j.dom.message.WSSecUsernameToken;
38  import org.apache.wss4j.dom.message.WSSecSignature;
39  import org.apache.wss4j.dom.util.WSSecurityUtil;
40  import org.w3c.dom.Document;
41  
42  /**
43   * Sign a request using a secret key derived from UsernameToken data.
44   *
45   * Enhanced by Alberto Coletti to support digest password type for
46   * username token signature
47   */
48  
49  public class UsernameTokenSignedAction implements Action {
50      public void execute(WSHandler handler, SecurityActionToken actionToken, RequestData reqData)
51              throws WSSecurityException {
52          CallbackHandler callbackHandler = reqData.getCallbackHandler();
53          if (callbackHandler == null) {
54              callbackHandler = handler.getPasswordCallbackHandler(reqData);
55          }
56          WSPasswordCallback passwordCallback =
57              handler.getPasswordCB(reqData.getUsername(), WSConstants.UT_SIGN, callbackHandler, reqData);
58  
59          if (reqData.getUsername() == null) {
60              throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noUser");
61          }
62  
63          WSSecUsernameToken builder = new WSSecUsernameToken(reqData.getSecHeader());
64          builder.setIdAllocator(reqData.getWssConfig().getIdAllocator());
65          builder.setPrecisionInMilliSeconds(reqData.isPrecisionInMilliSeconds());
66          builder.setWsTimeSource(reqData.getWssConfig().getCurrentTime());
67          builder.setWsDocInfo(reqData.getWsDocInfo());
68          builder.setExpandXopInclude(reqData.isExpandXopInclude());
69  
70          int iterations = reqData.getDerivedKeyIterations();
71          builder.addDerivedKey(iterations);
72  
73          builder.setUserInfo(reqData.getUsername(), passwordCallback.getPassword());
74          builder.addCreated();
75          builder.addNonce();
76          boolean useMac = reqData.isUseDerivedKeyForMAC();
77          byte[] salt = UsernameTokenUtil.generateSalt(useMac);
78          builder.prepare(salt);
79  
80          // Now prepare to sign.
81          // First step:  Get a WS Signature object and set config parameters
82          // second step: set user data and algorithm parameters. This
83          //              _must_ be done before we "prepare"
84          // third step:  Call "prepare". This creates the internal WS Signature
85          //              data structures, XML element, fills in the algorithms
86          //              and other data.
87          // fourth step: Get the references. These references identify the parts
88          //              of the document that will be included into the
89          //              signature. If no references are given sign the message
90          //              body by default.
91          // fifth step:  compute the signature
92          //
93          // after "prepare" the Signature XML element is ready and may prepend
94          // this to the security header.
95  
96          SignatureActionToken signatureToken = null;
97          if (actionToken instanceof SignatureActionToken) {
98              signatureToken = (SignatureActionToken)actionToken;
99          }
100         if (signatureToken == null) {
101             signatureToken = reqData.getSignatureToken();
102         }
103 
104         WSSecSignature sign = new WSSecSignature(reqData.getSecHeader());
105         sign.setIdAllocator(reqData.getWssConfig().getIdAllocator());
106         sign.setAddInclusivePrefixes(reqData.isAddInclusivePrefixes());
107 
108         sign.setCustomTokenValueType(WSConstants.USERNAMETOKEN_NS + "#UsernameToken");
109         sign.setCustomTokenId(builder.getId());
110         sign.setSecretKey(builder.getDerivedKey(salt));
111         sign.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
112         if (signatureToken.getDigestAlgorithm() != null) {
113             sign.setDigestAlgo(signatureToken.getDigestAlgorithm());
114         }
115 
116         if (signatureToken.getSignatureAlgorithm() != null) {
117             sign.setSignatureAlgorithm(signatureToken.getSignatureAlgorithm());
118         } else {
119             sign.setSignatureAlgorithm(WSConstants.HMAC_SHA1);
120         }
121 
122         sign.prepare(null);
123 
124         // prepend in this order: first the Signature Element and then the
125         // UsernameToken Element. This way the server gets the UsernameToken
126         // first, can check it and are prepared to compute the Signature key.
127         // sign.prependToHeader(reqData.getSecHeader());
128         // builder.prependToHeader(reqData.getSecHeader());
129 
130         List<WSEncryptionPart> parts = null;
131         if (!signatureToken.getParts().isEmpty()) {
132             parts = signatureToken.getParts();
133         } else {
134             parts = new ArrayList<>(1);
135             Document doc = reqData.getSecHeader().getSecurityHeaderElement().getOwnerDocument();
136             parts.add(WSSecurityUtil.getDefaultEncryptionPart(doc));
137         }
138         List<javax.xml.crypto.dsig.Reference> referenceList = sign.addReferencesToSign(parts);
139 
140         try {
141             sign.computeSignature(referenceList);
142             reqData.getSignatureValues().add(sign.getSignatureValue());
143         } catch (WSSecurityException e) {
144             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e,
145                     "empty", new Object[] {"WSHandler: Error during UsernameTokenSignature"}
146             );
147         }
148         builder.prependToHeader();
149 
150         Arrays.fill(salt, (byte)0);
151     }
152 }