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