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;
21  
22  import java.io.IOException;
23  import java.util.Collections;
24  
25  import javax.crypto.KeyGenerator;
26  import javax.crypto.SecretKey;
27  import javax.security.auth.callback.Callback;
28  import javax.security.auth.callback.CallbackHandler;
29  import javax.security.auth.callback.UnsupportedCallbackException;
30  import javax.xml.crypto.dsig.SignatureMethod;
31  
32  import org.apache.wss4j.common.util.SOAPUtil;
33  import org.apache.wss4j.dom.WSConstants;
34  import org.apache.wss4j.dom.common.CustomHandler;
35  import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
36  import org.apache.wss4j.dom.common.SecretKeyCallbackHandler;
37  
38  import org.apache.wss4j.dom.engine.WSSConfig;
39  import org.apache.wss4j.dom.engine.WSSecurityEngine;
40  import org.apache.wss4j.common.crypto.Crypto;
41  import org.apache.wss4j.common.crypto.CryptoFactory;
42  import org.apache.wss4j.common.ext.WSPasswordCallback;
43  import org.apache.wss4j.common.util.KeyUtils;
44  import org.apache.wss4j.common.util.XMLUtils;
45  import org.apache.wss4j.dom.handler.HandlerAction;
46  import org.apache.wss4j.dom.handler.RequestData;
47  import org.apache.wss4j.dom.handler.WSHandlerConstants;
48  
49  import org.junit.jupiter.api.BeforeEach;
50  import org.junit.jupiter.api.Test;
51  import org.w3c.dom.Document;
52  
53  
54  /**
55   * Test symmetric key signature created using an encrypted key
56   * Demonstrates that Signature Crypto object can have null values when
57   * calling processSecurityHeader method of WSSecurityEngine.
58   */
59  public class SymmetricSignatureTest implements CallbackHandler {
60      private static final org.slf4j.Logger LOG =
61          org.slf4j.LoggerFactory.getLogger(SymmetricSignatureTest.class);
62      private WSSecurityEngine secEngine = new WSSecurityEngine();
63      private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
64      private SecretKeyCallbackHandler secretKeyCallbackHandler = new SecretKeyCallbackHandler();
65      private byte[] keyData;
66      private Crypto crypto;
67  
68      public SymmetricSignatureTest() throws Exception {
69          WSSConfig.init();
70          crypto = CryptoFactory.getInstance("wss40.properties");
71      }
72  
73      /**
74       * Setup method
75       * <p/>
76       *
77       * @throws Exception Thrown when there is a problem in setup
78       */
79      @BeforeEach
80      public void setUp() throws Exception {
81          KeyGenerator keyGen = KeyGenerator.getInstance("AES");
82          keyGen.init(128);
83          SecretKey key = keyGen.generateKey();
84          keyData = key.getEncoded();
85      }
86  
87      /**
88       * Test signing a message body using a symmetric key with EncryptedKeySHA1
89       */
90      @Test
91      public void testSymmetricSignatureSHA1() throws Exception {
92          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
93  
94          WSSecHeader secHeader = new WSSecHeader(doc);
95          secHeader.insertSecurityHeader();
96  
97          WSSecSignature sign = new WSSecSignature(secHeader);
98          sign.setKeyIdentifierType(WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER);
99          sign.setSecretKey(keyData);
100         sign.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
101 
102         Document signedDoc = sign.build(crypto);
103 
104         byte[] encodedBytes = KeyUtils.generateDigest(keyData);
105         String identifier = org.apache.xml.security.utils.XMLUtils.encodeToString(encodedBytes);
106         secretKeyCallbackHandler.addSecretKey(identifier, keyData);
107 
108         if (LOG.isDebugEnabled()) {
109             LOG.debug("Signed symmetric message SHA1:");
110             String outputString =
111                 XMLUtils.prettyDocumentToString(signedDoc);
112             LOG.debug(outputString);
113         }
114 
115         secEngine.processSecurityHeader(doc, null, secretKeyCallbackHandler, null, crypto);
116     }
117 
118 
119     /**
120      * Test signing a message body using a symmetric key with Direct Reference to an
121      * EncryptedKey
122      */
123     @Test
124     public void testSymmetricSignatureDR() throws Exception {
125         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
126 
127         WSSecHeader secHeader = new WSSecHeader(doc);
128         secHeader.insertSecurityHeader();
129 
130         WSSecEncryptedKey encrKey = new WSSecEncryptedKey(secHeader);
131         encrKey.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
132         encrKey.setUserInfo("wss40", "security");
133 
134         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_192);
135         SecretKey symmetricKey = keyGen.generateKey();
136         encrKey.prepare(crypto, symmetricKey);
137 
138         WSSecSignature sign = new WSSecSignature(secHeader);
139         sign.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
140         sign.setCustomTokenId(encrKey.getId());
141         sign.setSecretKey(symmetricKey.getEncoded());
142         sign.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
143         sign.setCustomTokenValueType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
144 
145         Document signedDoc = sign.build(crypto);
146         encrKey.prependToHeader();
147 
148         if (LOG.isDebugEnabled()) {
149             LOG.debug("Signed symmetric message DR:");
150             String outputString =
151                 XMLUtils.prettyDocumentToString(signedDoc);
152             LOG.debug(outputString);
153         }
154 
155         verify(signedDoc);
156     }
157 
158     /**
159      * Test that first signs, then encrypts a WS-Security envelope.
160      * <p/>
161      *
162      * @throws Exception Thrown when there is any problem in signing, encryption,
163      *                   decryption, or verification
164      */
165     @Test
166     public void testEncryptedKeySignature() throws Exception {
167         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
168         LOG.info("Before Sign/Encryption....");
169 
170         WSSecHeader secHeader = new WSSecHeader(doc);
171         secHeader.insertSecurityHeader();
172 
173         WSSecEncryptedKey encrKey = new WSSecEncryptedKey(secHeader);
174         encrKey.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
175         encrKey.setUserInfo("wss40", "security");
176 
177         KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_192);
178         SecretKey symmetricKey = keyGen.generateKey();
179         encrKey.prepare(crypto, symmetricKey);
180 
181         WSSecEncrypt encrypt = new WSSecEncrypt(secHeader);
182         encrypt.setEncKeyId(encrKey.getId());
183         encrypt.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
184         encrypt.setEncryptSymmKey(false);
185         encrypt.setEncryptedKeyElement(encrKey.getEncryptedKeyElement());
186 
187         WSSecSignature sign = new WSSecSignature(secHeader);
188         sign.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
189         sign.setCustomTokenId(encrKey.getId());
190         sign.setCustomTokenValueType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
191         sign.setSecretKey(symmetricKey.getEncoded());
192         sign.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
193 
194         sign.build(crypto);
195         Document encryptedSignedDoc = encrypt.build(crypto, symmetricKey);
196 
197         if (LOG.isDebugEnabled()) {
198             LOG.debug("Signed and encrypted message with IssuerSerial key identifier (both), 3DES:");
199             String outputString =
200                 XMLUtils.prettyDocumentToString(encryptedSignedDoc);
201             LOG.debug(outputString);
202         }
203 
204         LOG.info("After Sign/Encryption....");
205         verify(encryptedSignedDoc);
206     }
207 
208     /**
209      * Test signing a message body using a symmetric key with EncryptedKeySHA1.
210      * The request is generated using WSHandler, instead of coding it.
211      */
212     @Test
213     public void testSymmetricSignatureSHA1Handler() throws Exception {
214         final WSSConfig cfg = WSSConfig.getNewInstance();
215         RequestData reqData = new RequestData();
216         reqData.setWssConfig(cfg);
217         java.util.Map<String, Object> messageContext = new java.util.TreeMap<>();
218         messageContext.put(WSHandlerConstants.SIG_KEY_ID, "EncryptedKeySHA1");
219         messageContext.put(WSHandlerConstants.SIG_ALGO, SignatureMethod.HMAC_SHA1);
220         messageContext.put(WSHandlerConstants.PW_CALLBACK_REF, this);
221         reqData.setMsgContext(messageContext);
222         reqData.setUsername("");
223 
224         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
225         CustomHandler handler = new CustomHandler();
226         HandlerAction action = new HandlerAction(WSConstants.SIGN);
227         handler.send(
228             doc,
229             reqData,
230             Collections.singletonList(action),
231             true
232         );
233 
234         String outputString =
235             XMLUtils.prettyDocumentToString(doc);
236         if (LOG.isDebugEnabled()) {
237             LOG.debug(outputString);
238         }
239 
240         reqData = new RequestData();
241         reqData.setWssConfig(WSSConfig.getNewInstance());
242         messageContext = new java.util.TreeMap<>();
243         messageContext.put(WSHandlerConstants.PW_CALLBACK_REF, this);
244         reqData.setMsgContext(messageContext);
245         reqData.setUsername("");
246 
247         handler.receive(Collections.singletonList(WSConstants.SIGN), reqData);
248 
249         secEngine.processSecurityHeader(doc, null, this, null, crypto);
250     }
251 
252 
253     /**
254      * Verifies the soap envelope
255      * <p/>
256      *
257      * @param doc
258      * @throws Exception Thrown when there is a problem in verification
259      */
260     private void verify(Document doc) throws Exception {
261         secEngine.processSecurityHeader(doc, null, callbackHandler, null, crypto);
262         if (LOG.isDebugEnabled()) {
263             LOG.debug("Verfied and decrypted message:");
264             String outputString =
265                 XMLUtils.prettyDocumentToString(doc);
266             LOG.debug(outputString);
267         }
268     }
269 
270     public void handle(Callback[] callbacks)
271         throws IOException, UnsupportedCallbackException {
272         for (Callback callback : callbacks) {
273             if (callback instanceof WSPasswordCallback) {
274                 WSPasswordCallback pc = (WSPasswordCallback) callback;
275                 /*
276                  * here call a function/method to lookup the password for
277                  * the given identifier (e.g. a user name or keystore alias)
278                  * e.g.: pc.setPassword(passStore.getPassword(pc.getIdentfifier))
279                  * for Testing we supply a fixed name here.
280                  */
281                 pc.setPassword("security");
282                 pc.setKey(keyData);
283             } else {
284                 throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
285             }
286         }
287     }
288 
289 
290 }