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.message.token;
21  
22  import org.apache.ws.security.WSEncryptionPart;
23  import org.apache.ws.security.WSSConfig;
24  import org.apache.ws.security.WSSecurityEngine;
25  import org.apache.ws.security.WSConstants;
26  import org.apache.ws.security.WSSecurityEngineResult;
27  import org.apache.ws.security.WSSecurityException;
28  import org.apache.ws.security.common.KeystoreCallbackHandler;
29  import org.apache.ws.security.common.SOAPUtil;
30  import org.apache.ws.security.components.crypto.Crypto;
31  import org.apache.ws.security.components.crypto.CryptoFactory;
32  import org.apache.ws.security.handler.RequestData;
33  import org.apache.ws.security.message.WSSecEncrypt;
34  import org.apache.ws.security.message.WSSecSignature;
35  import org.apache.ws.security.message.WSSecHeader;
36  import org.apache.ws.security.message.WSSecTimestamp;
37  import org.apache.ws.security.util.Base64;
38  import org.apache.ws.security.util.WSSecurityUtil;
39  import org.apache.ws.security.validate.Credential;
40  import org.apache.ws.security.validate.Validator;
41  import org.w3c.dom.Document;
42  
43  import javax.crypto.KeyGenerator;
44  import javax.crypto.SecretKey;
45  import javax.security.auth.callback.CallbackHandler;
46  import javax.xml.crypto.dsig.SignatureMethod;
47  
48  import java.util.ArrayList;
49  import java.util.Arrays;
50  import java.util.List;
51  
52  /**
53   * This is a test for the Kerberos Token Profile 1.1
54   */
55  public class BSTKerberosTest extends org.junit.Assert {
56      private static final org.apache.commons.logging.Log LOG = 
57          org.apache.commons.logging.LogFactory.getLog(BSTKerberosTest.class);
58      private static final String AP_REQ = WSConstants.WSS_GSS_KRB_V5_AP_REQ;
59      private static final String BASE64_NS = 
60          WSConstants.SOAPMESSAGE_NS + "#Base64Binary";
61      private WSSecurityEngine secEngine = new WSSecurityEngine();
62      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
63      private Crypto crypto = null;
64      
65      public BSTKerberosTest() throws Exception {
66          WSSConfig.init();
67          crypto = CryptoFactory.getInstance();
68      }
69  
70      /**
71       * A unit test for creating BinarySecurityTokens
72       */
73      @org.junit.Test
74      public void testCreateBinarySecurityToken() throws Exception {
75          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
76  
77          WSSecHeader secHeader = new WSSecHeader();
78          secHeader.insertSecurityHeader(doc);
79          
80          BinarySecurity bst = new BinarySecurity(doc);
81          bst.setValueType(AP_REQ);
82          bst.setEncodingType(BASE64_NS);
83          bst.setToken("12345678".getBytes());
84          WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
85          
86          if (LOG.isDebugEnabled()) {
87              String outputString = 
88                  org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
89              LOG.debug(outputString);
90          }
91          
92          assertTrue(AP_REQ.equals(bst.getValueType()));
93          assertTrue(BASE64_NS.equals(bst.getEncodingType()));
94          assertTrue(bst.getToken() != null);
95      }
96      
97      
98      /**
99       * A test for signing a Kerberos BST
100      */
101     @org.junit.Test
102     public void testSignBST() throws Exception {
103         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
104 
105         WSSecHeader secHeader = new WSSecHeader();
106         secHeader.insertSecurityHeader(doc);
107         
108         BinarySecurity bst = new BinarySecurity(doc);
109         bst.setValueType(AP_REQ);
110         bst.setEncodingType(BASE64_NS);
111         bst.setToken("12345678".getBytes());
112         bst.setID("Id-" + bst.hashCode());
113         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
114         
115         WSSecSignature sign = new WSSecSignature();
116         sign.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
117         sign.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
118         
119         java.util.List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
120         WSEncryptionPart encP =
121             new WSEncryptionPart(bst.getID());
122         parts.add(encP);
123         sign.setParts(parts);
124         
125         Document signedDoc = sign.build(doc, crypto, secHeader);
126         
127         if (LOG.isDebugEnabled()) {
128             String outputString = 
129                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
130             LOG.debug(outputString);
131         }
132         
133         verify(signedDoc);
134     }
135     
136     /**
137      * A test for signing a Kerberos BST as well as a Timestamp
138      */
139     @org.junit.Test
140     public void testSignBSTTimestamp() throws Exception {
141         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
142 
143         WSSecHeader secHeader = new WSSecHeader();
144         secHeader.insertSecurityHeader(doc);
145         
146         BinarySecurity bst = new BinarySecurity(doc);
147         bst.setValueType(AP_REQ);
148         bst.setEncodingType(BASE64_NS);
149         bst.setToken("12345678".getBytes());
150         bst.setID("Id-" + bst.hashCode());
151         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
152         
153         WSSecTimestamp timestamp = new WSSecTimestamp();
154         timestamp.setTimeToLive(600);
155         timestamp.build(doc, secHeader);
156         
157         WSSecSignature sign = new WSSecSignature();
158         sign.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
159         sign.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
160         
161         java.util.List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
162         parts.add(new WSEncryptionPart(bst.getID()));
163         parts.add(new WSEncryptionPart(timestamp.getId()));
164         sign.setParts(parts);
165         
166         Document signedDoc = sign.build(doc, crypto, secHeader);
167         
168         if (LOG.isDebugEnabled()) {
169             String outputString = 
170                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
171             LOG.debug(outputString);
172         }
173         
174         verify(signedDoc);
175     }
176     
177     /**
178      * Test Validating a Kerberos BinarySecurityToken
179      */
180     @org.junit.Test
181     public void testProcessToken() throws Exception {
182         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
183 
184         WSSecHeader secHeader = new WSSecHeader();
185         secHeader.insertSecurityHeader(doc);
186         
187         BinarySecurity bst = new BinarySecurity(doc);
188         bst.setValueType(AP_REQ);
189         bst.setEncodingType(BASE64_NS);
190         bst.setToken("12345678".getBytes());
191         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
192         
193         if (LOG.isDebugEnabled()) {
194             String outputString = 
195                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
196             LOG.debug(outputString);
197         }
198         
199         List<WSSecurityEngineResult> results = verify(doc);
200         WSSecurityEngineResult actionResult =
201             WSSecurityUtil.fetchActionResult(results, WSConstants.BST);
202         BinarySecurity token =
203             (BinarySecurity)actionResult.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
204         assertTrue(token != null);
205         
206         assertTrue(AP_REQ.equals(token.getValueType()));
207         assertTrue(BASE64_NS.equals(token.getEncodingType()));
208         assertTrue(token.getToken() != null);
209     }
210     
211     /**
212      * Test Validating a Kerberos BinarySecurityToken using a custom Validator instance.
213      */
214     @org.junit.Test
215     public void testProcessTokenCustomValidator() throws Exception {
216         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
217 
218         WSSecHeader secHeader = new WSSecHeader();
219         secHeader.insertSecurityHeader(doc);
220         
221         BinarySecurity bst = new BinarySecurity(doc);
222         bst.setValueType(AP_REQ);
223         bst.setEncodingType(BASE64_NS);
224         bst.setToken("12345678".getBytes());
225         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
226         
227         if (LOG.isDebugEnabled()) {
228             String outputString = 
229                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
230             LOG.debug(outputString);
231         }
232         
233         WSSecurityEngine customEngine = new WSSecurityEngine();
234         WSSConfig wssConfig = WSSConfig.getNewInstance();
235         wssConfig.setValidator(WSSecurityEngine.BINARY_TOKEN, new KerberosValidator());
236         customEngine.setWssConfig(wssConfig);
237         customEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
238         
239         bst.setToken("12345679".getBytes());
240         try {
241             customEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
242             fail("Failure expected on a bad token");
243         } catch (WSSecurityException ex) {
244             // expected
245         }
246     }
247     
248     /**
249      * A test for signing using a direct reference to a Kerberos token
250      */
251     @org.junit.Test
252     public void testKerberosSignatureDRCreation() throws Exception {
253         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
254 
255         WSSecHeader secHeader = new WSSecHeader();
256         secHeader.insertSecurityHeader(doc);
257         
258         BinarySecurity bst = new BinarySecurity(doc);
259         bst.setValueType(AP_REQ);
260         bst.setEncodingType(BASE64_NS);
261         
262         KeyGenerator keyGen = KeyGenerator.getInstance("AES");
263         keyGen.init(128);
264         SecretKey key = keyGen.generateKey();
265         byte[] keyData = key.getEncoded();
266         
267         bst.setToken(keyData);
268         bst.setID("Id-" + bst.hashCode());
269         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
270         
271         WSSecSignature sign = new WSSecSignature();
272         sign.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
273         sign.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
274         sign.setCustomTokenValueType(AP_REQ);
275         sign.setCustomTokenId(bst.getID());
276         sign.setSecretKey(keyData);
277         
278         Document signedDoc = sign.build(doc, crypto, secHeader);
279         
280         if (LOG.isDebugEnabled()) {
281             String outputString = 
282                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
283             LOG.debug(outputString);
284         }
285     }
286     
287     /**
288      * A test for signing using a KeyIdentifier to a Kerberos token
289      */
290     @org.junit.Test
291     public void testKerberosSignatureKICreation() throws Exception {
292         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
293 
294         WSSecHeader secHeader = new WSSecHeader();
295         secHeader.insertSecurityHeader(doc);
296         
297         BinarySecurity bst = new BinarySecurity(doc);
298         bst.setValueType(AP_REQ);
299         bst.setEncodingType(BASE64_NS);
300         
301         KeyGenerator keyGen = KeyGenerator.getInstance("AES");
302         keyGen.init(128);
303         SecretKey key = keyGen.generateKey();
304         byte[] keyData = key.getEncoded();
305         
306         bst.setToken(keyData);
307         bst.setID("Id-" + bst.hashCode());
308         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
309         
310         WSSecSignature sign = new WSSecSignature();
311         sign.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
312         sign.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER);
313         sign.setCustomTokenValueType(WSConstants.WSS_KRB_KI_VALUE_TYPE);
314         
315         byte[] digestBytes = WSSecurityUtil.generateDigest(keyData);
316         sign.setCustomTokenId(Base64.encode(digestBytes));
317         sign.setSecretKey(keyData);
318         
319         Document signedDoc = sign.build(doc, crypto, secHeader);
320         
321         if (LOG.isDebugEnabled()) {
322             String outputString = 
323                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
324             LOG.debug(outputString);
325         }
326     }
327     
328     /**
329      * A test for encryption using a direct reference to a Kerberos token
330      */
331     @org.junit.Test
332     public void testKerberosEncryptionDRCreation() throws Exception {
333         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
334 
335         WSSecHeader secHeader = new WSSecHeader();
336         secHeader.insertSecurityHeader(doc);
337         
338         BinarySecurity bst = new BinarySecurity(doc);
339         bst.setValueType(AP_REQ);
340         bst.setEncodingType(BASE64_NS);
341         
342         KeyGenerator keyGen = KeyGenerator.getInstance("AES");
343         keyGen.init(128);
344         SecretKey key = keyGen.generateKey();
345         byte[] keyData = key.getEncoded();
346         
347         bst.setToken(keyData);
348         bst.setID("Id-" + bst.hashCode());
349         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
350         
351         WSSecEncrypt builder = new WSSecEncrypt();
352         builder.setSymmetricEncAlgorithm(WSConstants.AES_128);
353         builder.setSymmetricKey(key);
354         builder.setEncryptSymmKey(false);
355         builder.setCustomReferenceValue(AP_REQ);
356         builder.setEncKeyId(bst.getID());
357         Document encryptedDoc = builder.build(doc, crypto, secHeader);
358         
359         if (LOG.isDebugEnabled()) {
360             String outputString = 
361                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
362             LOG.debug(outputString);
363         }
364     }
365     
366     /**
367      * A test for encryption using a Key Identifier to a Kerberos token
368      */
369     @org.junit.Test
370     public void testKerberosEncryptionKICreation() throws Exception {
371         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
372 
373         WSSecHeader secHeader = new WSSecHeader();
374         secHeader.insertSecurityHeader(doc);
375         
376         BinarySecurity bst = new BinarySecurity(doc);
377         bst.setValueType(AP_REQ);
378         bst.setEncodingType(BASE64_NS);
379         
380         KeyGenerator keyGen = KeyGenerator.getInstance("AES");
381         keyGen.init(128);
382         SecretKey key = keyGen.generateKey();
383         byte[] keyData = key.getEncoded();
384         
385         bst.setToken(keyData);
386         bst.setID("Id-" + bst.hashCode());
387         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bst.getElement());
388         
389         WSSecEncrypt builder = new WSSecEncrypt();
390         builder.setSymmetricEncAlgorithm(WSConstants.AES_128);
391         builder.setSymmetricKey(key);
392         builder.setEncryptSymmKey(false);
393         builder.setCustomReferenceValue(WSConstants.WSS_KRB_KI_VALUE_TYPE);
394         
395         byte[] digestBytes = WSSecurityUtil.generateDigest(keyData);
396         builder.setEncKeyId(Base64.encode(digestBytes));
397         
398         Document encryptedDoc = builder.build(doc, crypto, secHeader);
399         
400         if (LOG.isDebugEnabled()) {
401             String outputString = 
402                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(encryptedDoc);
403             LOG.debug(outputString);
404         }
405     }
406 
407 
408     
409     /**
410      * Verifies the soap envelope
411      * <p/>
412      * 
413      * @param doc 
414      * @throws Exception Thrown when there is a problem in verification
415      */
416     private List<WSSecurityEngineResult> verify(Document doc) throws Exception {
417         List<WSSecurityEngineResult> results = 
418             secEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
419         if (LOG.isDebugEnabled()) {
420             LOG.debug("Verfied and decrypted message:");
421             String outputString = 
422                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
423             LOG.debug(outputString);
424         }
425         return results;
426     }
427 
428     
429     /**
430      * A dummy validator for a Kerberos BST token.
431      */
432     private static class KerberosValidator implements Validator {
433 
434         public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
435             BinarySecurity token = credential.getBinarySecurityToken();
436             if (token == null) {
437                 throw new WSSecurityException(WSSecurityException.FAILURE);
438             }
439 
440             if (!AP_REQ.equals(token.getValueType())) {
441                 throw new WSSecurityException(WSSecurityException.FAILURE);
442             }
443             
444             byte[] tokenBytes = token.getToken();
445             if (!Arrays.equals(tokenBytes, "12345678".getBytes())) {
446                 throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
447             }
448             return credential;
449         }
450         
451     }
452     
453 }