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