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.misc;
21  
22  import java.io.IOException;
23  
24  import javax.crypto.KeyGenerator;
25  import javax.crypto.SecretKey;
26  import javax.security.auth.callback.Callback;
27  import javax.security.auth.callback.CallbackHandler;
28  import javax.security.auth.callback.UnsupportedCallbackException;
29  import javax.xml.namespace.QName;
30  
31  import org.apache.wss4j.common.bsp.BSPEnforcer;
32  import org.apache.wss4j.common.crypto.Crypto;
33  import org.apache.wss4j.common.crypto.CryptoFactory;
34  import org.apache.wss4j.common.ext.WSPasswordCallback;
35  import org.apache.wss4j.common.ext.WSSecurityException;
36  import org.apache.wss4j.common.token.Reference;
37  import org.apache.wss4j.common.util.KeyUtils;
38  import org.apache.wss4j.common.util.SOAPUtil;
39  import org.apache.wss4j.dom.WSConstants;
40  
41  import org.apache.wss4j.dom.engine.WSSConfig;
42  import org.apache.wss4j.dom.engine.WSSecurityEngine;
43  import org.apache.wss4j.dom.message.WSSecEncrypt;
44  import org.apache.wss4j.dom.message.WSSecHeader;
45  import org.apache.wss4j.dom.message.WSSecTimestamp;
46  import org.apache.wss4j.dom.message.WSSecUsernameToken;
47  import org.apache.wss4j.dom.message.token.UsernameToken;
48  
49  import org.junit.jupiter.api.Test;
50  import org.w3c.dom.Document;
51  
52  import static org.junit.jupiter.api.Assertions.assertEquals;
53  import static org.junit.jupiter.api.Assertions.assertTrue;
54  import static org.junit.jupiter.api.Assertions.fail;
55  
56  /**
57   * WS-Security Test Case for fault codes. The SOAP Message Security specification 1.1 defines
58   * standard fault codes and fault strings for error propagation.
59   */
60  public class FaultCodeTest implements CallbackHandler {
61      private WSSecurityEngine secEngine = new WSSecurityEngine();
62      private Crypto crypto;
63  
64      public FaultCodeTest() throws Exception {
65          crypto = CryptoFactory.getInstance("wss40.properties");
66          WSSConfig.init();
67      }
68  
69      /**
70       * Test for the wsse:FailedCheck faultcode. This will fail due to a bad password in
71       * the callback handler.
72       */
73      @Test
74      public void testFailedCheck() throws Exception {
75          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
76          WSSecHeader secHeader = new WSSecHeader(doc);
77          secHeader.insertSecurityHeader();
78  
79          WSSecEncrypt builder = new WSSecEncrypt(secHeader);
80          builder.setUserInfo("wss40", "security");
81          builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
82  
83          KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128);
84          SecretKey symmetricKey = keyGen.generateKey();
85          Document encryptedDoc = builder.build(crypto, symmetricKey);
86  
87          try {
88              verify(encryptedDoc);
89              fail("Failure expected with a bad password");
90          } catch (WSSecurityException ex) {
91              assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
92              assertEquals("The private key for the supplied alias does not exist in the keystore", ex.getMessage());
93              QName faultCode = new QName(WSConstants.WSSE_NS, "FailedCheck");
94              assertTrue(ex.getFaultCode().equals(faultCode));
95          }
96      }
97  
98      /**
99       * Test for the wsse:UnsupportedAlgorithm faultcode. This will fail due to the argument
100      * passed to getCipherInstance.
101      */
102     @Test
103     public void testUnsupportedAlgorithm() throws Exception {
104         try {
105             secEngine.getWssConfig();
106             KeyUtils.getCipherInstance("Bad Algorithm");
107             fail("Failure expected on an unsupported algorithm");
108         } catch (WSSecurityException ex) {
109             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM);
110             assertEquals("unsupported key transport encryption algorithm: No such algorithm: \"Bad Algorithm\"", ex.getMessage());
111             QName faultCode = new QName(WSConstants.WSSE_NS, "UnsupportedAlgorithm");
112             assertTrue(ex.getFaultCode().equals(faultCode));
113         }
114     }
115 
116     /**
117      * Test for the wsse:MessageExpired faultcode. This will fail due to the argument
118      * passed to setTimeToLive.
119      */
120     @Test
121     public void testMessageExpired() throws Exception {
122         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
123         WSSecHeader secHeader = new WSSecHeader(doc);
124         secHeader.insertSecurityHeader();
125 
126         WSSecTimestamp builder = new WSSecTimestamp(secHeader);
127         builder.setTimeToLive(-1);
128 
129         Document timestampedDoc = builder.build();
130 
131         try {
132             verify(timestampedDoc);
133             fail("Failure expected on an expired message");
134         } catch (WSSecurityException ex) {
135             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
136             assertEquals("Invalid timestamp: The message timestamp has expired", ex.getMessage());
137             QName faultCode = new QName(WSConstants.WSSE_NS, "MessageExpired");
138             assertTrue(ex.getFaultCode().equals(faultCode));
139         }
140     }
141 
142     /**
143      * Test for the wsse:FailedAuthentication faultcode. This will fail due to a bad password in
144      * the callback handler.
145      */
146     @Test
147     public void testFailedAuthentication() throws Exception {
148         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
149         WSSecHeader secHeader = new WSSecHeader(doc);
150         secHeader.insertSecurityHeader();
151 
152         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
153         builder.addCreated();
154         builder.addNonce();
155         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
156 
157         Document timestampedDoc = builder.build();
158 
159         try {
160             verify(timestampedDoc);
161             fail("Failure expected on a bad password");
162         } catch (WSSecurityException ex) {
163             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
164             assertEquals("The security token could not be authenticated or authorized", ex.getMessage());
165             QName faultCode = new QName(WSConstants.WSSE_NS, "FailedAuthentication");
166             assertTrue(ex.getFaultCode().equals(faultCode));
167         }
168     }
169 
170     /**
171      * Test for the wsse:InvalidSecurityToken faultcode. This will fail due to the fact
172      * that a null username is used.
173      */
174     @Test
175     public void testInvalidSecurityToken() throws Exception {
176         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
177         WSSecHeader secHeader = new WSSecHeader(doc);
178         secHeader.insertSecurityHeader();
179 
180         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
181         builder.addCreated();
182         builder.addNonce();
183         builder.setUserInfo(null, "security");
184 
185         builder.build();
186 
187         try {
188             new UsernameToken(doc.getDocumentElement(), false, new BSPEnforcer());
189             fail("Failure expected on an invalid security token");
190         } catch (WSSecurityException ex) {
191             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN);
192             assertEquals("Bad element, expected \"{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}UsernameToken\" while got \"{http://schemas.xmlsoap.org/soap/envelope/}Envelope\"", ex.getMessage());
193             QName faultCode = new QName(WSConstants.WSSE_NS, "InvalidSecurityToken");
194             assertTrue(ex.getFaultCode().equals(faultCode));
195         }
196     }
197 
198     /**
199      * Test for the wsse:InvalidSecurity faultcode.
200      */
201     @Test
202     public void testInvalidSecurity() throws Exception {
203         try {
204             new Reference((org.w3c.dom.Element)null);
205             fail("Failure expected on processing the security header");
206         } catch (WSSecurityException ex) {
207             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
208             assertEquals("<Reference> token could not be retrieved", ex.getMessage());
209             QName faultCode = new QName(WSConstants.WSSE_NS, "InvalidSecurity");
210             assertTrue(ex.getFaultCode().equals(faultCode));
211         }
212     }
213 
214 
215     /**
216      * Verifies the soap envelope.
217      *
218      * @param doc soap envelope
219      * @throws Exception Thrown when there is a problem in verification
220      */
221     private void verify(Document doc) throws Exception {
222         secEngine.processSecurityHeader(doc, null, this, crypto);
223     }
224 
225 
226     public void handle(Callback[] callbacks)
227         throws IOException, UnsupportedCallbackException {
228         for (Callback callback : callbacks) {
229             if (callback instanceof WSPasswordCallback) {
230                 WSPasswordCallback pc = (WSPasswordCallback) callback;
231                 //
232                 // Deliberately wrong password
233                 //
234                 pc.setPassword("securit");
235             } else {
236                 throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
237             }
238         }
239     }
240 
241 }