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.util.ArrayList;
23  import java.util.List;
24  
25  import javax.crypto.KeyGenerator;
26  import javax.crypto.SecretKey;
27  import javax.xml.crypto.dsig.SignatureMethod;
28  
29  import org.apache.wss4j.common.SignatureActionToken;
30  import org.apache.wss4j.common.bsp.BSPRule;
31  import org.apache.wss4j.common.crypto.AlgorithmSuite;
32  import org.apache.wss4j.common.crypto.Crypto;
33  import org.apache.wss4j.common.crypto.CryptoFactory;
34  import org.apache.wss4j.common.ext.WSSecurityException;
35  import org.apache.wss4j.common.util.KeyUtils;
36  import org.apache.wss4j.common.util.SOAPUtil;
37  import org.apache.wss4j.common.util.XMLUtils;
38  import org.apache.wss4j.dom.WSConstants;
39  import org.apache.wss4j.dom.common.SecretKeyCallbackHandler;
40  
41  import org.apache.wss4j.dom.engine.WSSConfig;
42  import org.apache.wss4j.dom.engine.WSSecurityEngine;
43  import org.apache.wss4j.dom.handler.RequestData;
44  import org.apache.wss4j.dom.handler.WSHandlerResult;
45  import org.apache.wss4j.dom.util.WSSecurityUtil;
46  
47  import org.junit.jupiter.api.Test;
48  import org.w3c.dom.Document;
49  import org.w3c.dom.Element;
50  
51  import static org.junit.jupiter.api.Assertions.assertTrue;
52  import static org.junit.jupiter.api.Assertions.fail;
53  
54  /**
55   * A set of test-cases for signing and verifying SOAP requests when specifying an
56   * AlgorithmSuite policy.
57   */
58  public class SignatureAlgorithmSuiteTest {
59      private static final org.slf4j.Logger LOG =
60          org.slf4j.LoggerFactory.getLogger(SignatureAlgorithmSuiteTest.class);
61  
62      private Crypto crypto;
63  
64      public SignatureAlgorithmSuiteTest() throws Exception {
65          WSSConfig.init();
66          crypto = CryptoFactory.getInstance();
67      }
68  
69      @Test
70      public void testSignature() throws Exception {
71          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
72          WSSecHeader secHeader = new WSSecHeader(doc);
73          secHeader.insertSecurityHeader();
74  
75          WSSecSignature builder = new WSSecSignature(secHeader);
76          builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
77          builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
78          builder.setSignatureAlgorithm(WSConstants.RSA_SHA1);
79  
80          Document signedDoc = builder.build(crypto);
81  
82          if (LOG.isDebugEnabled()) {
83              String outputString =
84                  XMLUtils.prettyDocumentToString(signedDoc);
85              LOG.debug(outputString);
86          }
87  
88          Element securityHeader = WSSecurityUtil.getSecurityHeader(signedDoc, null);
89          AlgorithmSuite algorithmSuite = createAlgorithmSuite();
90  
91          verify(securityHeader, algorithmSuite, crypto);
92  
93          algorithmSuite.setMinimumAsymmetricKeyLength(1024);
94  
95          try {
96              verify(securityHeader, algorithmSuite, crypto);
97              fail("Expected failure as 512-bit keys are not allowed");
98          } catch (WSSecurityException ex) {
99              assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
100         }
101     }
102 
103     @Test
104     public void testSignatureMethodDSA() throws Exception {
105         Crypto dsaCrypto = CryptoFactory.getInstance("wss40.properties");
106 
107         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
108         WSSecHeader secHeader = new WSSecHeader(doc);
109         secHeader.insertSecurityHeader();
110 
111         WSSecSignature builder = new WSSecSignature(secHeader);
112         builder.setUserInfo("wss40DSA", "security");
113         builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
114         builder.setSignatureAlgorithm(WSConstants.DSA);
115 
116         Document signedDoc = builder.build(dsaCrypto);
117 
118         if (LOG.isDebugEnabled()) {
119             String outputString =
120                 XMLUtils.prettyDocumentToString(signedDoc);
121             LOG.debug(outputString);
122         }
123 
124         Element securityHeader = WSSecurityUtil.getSecurityHeader(signedDoc, null);
125         AlgorithmSuite algorithmSuite = createAlgorithmSuite();
126 
127         try {
128             verify(securityHeader, algorithmSuite, dsaCrypto);
129             fail("Expected failure as DSA is not allowed");
130         } catch (WSSecurityException ex) {
131             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
132         }
133 
134         algorithmSuite.addSignatureMethod(WSConstants.DSA);
135         verify(securityHeader, algorithmSuite, dsaCrypto);
136     }
137 
138     @Test
139     public void testSymmetricKey() throws Exception {
140 
141         KeyGenerator keyGen = KeyGenerator.getInstance("AES");
142         keyGen.init(128);
143         SecretKey key = keyGen.generateKey();
144         byte[] keyData = key.getEncoded();
145 
146         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
147         WSSecHeader secHeader = new WSSecHeader(doc);
148         secHeader.insertSecurityHeader();
149 
150         WSSecSignature builder = new WSSecSignature(secHeader);
151         builder.setKeyIdentifierType(WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER);
152         builder.setSecretKey(keyData);
153         builder.setSignatureAlgorithm(SignatureMethod.HMAC_SHA1);
154 
155         Document signedDoc = builder.build(crypto);
156 
157         if (LOG.isDebugEnabled()) {
158             String outputString =
159                 XMLUtils.prettyDocumentToString(signedDoc);
160             LOG.debug(outputString);
161         }
162 
163         byte[] encodedBytes = KeyUtils.generateDigest(keyData);
164         String identifier = org.apache.xml.security.utils.XMLUtils.encodeToString(encodedBytes);
165         SecretKeyCallbackHandler secretKeyCallbackHandler = new SecretKeyCallbackHandler();
166         secretKeyCallbackHandler.addSecretKey(identifier, keyData);
167 
168         Element securityHeader = WSSecurityUtil.getSecurityHeader(signedDoc, null);
169         AlgorithmSuite algorithmSuite = createAlgorithmSuite();
170 
171         WSSecurityEngine secEngine = new WSSecurityEngine();
172         RequestData data = new RequestData();
173         SignatureActionToken actionToken = new SignatureActionToken();
174         actionToken.setCrypto(crypto);
175         data.setSignatureToken(actionToken);
176         data.setCallbackHandler(secretKeyCallbackHandler);
177         data.setAlgorithmSuite(algorithmSuite);
178 
179         try {
180             secEngine.processSecurityHeader(securityHeader, data);
181             fail("Expected failure as HMAC-SHA1 is not allowed");
182         } catch (WSSecurityException ex) {
183             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
184         }
185 
186         algorithmSuite.addSignatureMethod(WSConstants.HMAC_SHA1);
187         secEngine.processSecurityHeader(securityHeader, data);
188 
189         algorithmSuite.setMinimumSymmetricKeyLength(256);
190         try {
191             secEngine.processSecurityHeader(securityHeader, data);
192             fail("Expected failure as a 128 bit key is not allowed");
193         } catch (WSSecurityException ex) {
194             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
195         }
196 
197         algorithmSuite.setMinimumSymmetricKeyLength(64);
198         algorithmSuite.setMaximumSymmetricKeyLength(120);
199         try {
200             secEngine.processSecurityHeader(securityHeader, data);
201             fail("Expected failure as a 128 bit key is not allowed");
202         } catch (WSSecurityException ex) {
203             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
204         }
205     }
206 
207     @Test
208     public void testC14nMethod() throws Exception {
209         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
210         WSSecHeader secHeader = new WSSecHeader(doc);
211         secHeader.insertSecurityHeader();
212 
213         WSSecSignature builder = new WSSecSignature(secHeader);
214         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
215         builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
216         builder.setSignatureAlgorithm(WSConstants.RSA_SHA1);
217         builder.setSigCanonicalization(WSConstants.C14N_EXCL_WITH_COMMENTS);
218 
219         Document signedDoc = builder.build(crypto);
220 
221         if (LOG.isDebugEnabled()) {
222             String outputString =
223                 XMLUtils.prettyDocumentToString(signedDoc);
224             LOG.debug(outputString);
225         }
226 
227         Element securityHeader = WSSecurityUtil.getSecurityHeader(signedDoc, null);
228         AlgorithmSuite algorithmSuite = createAlgorithmSuite();
229 
230         try {
231             verify(securityHeader, algorithmSuite, crypto);
232             fail("Expected failure as C14n algorithm is not allowed");
233         } catch (WSSecurityException ex) {
234             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
235         }
236 
237         algorithmSuite.addC14nAlgorithm(WSConstants.C14N_EXCL_WITH_COMMENTS);
238         verify(securityHeader, algorithmSuite, crypto);
239     }
240 
241     @Test
242     public void testDigestMethod() throws Exception {
243         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
244         WSSecHeader secHeader = new WSSecHeader(doc);
245         secHeader.insertSecurityHeader();
246 
247         WSSecSignature builder = new WSSecSignature(secHeader);
248         builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
249         builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
250         builder.setSignatureAlgorithm(WSConstants.RSA_SHA1);
251         builder.setDigestAlgo(WSConstants.SHA256);
252 
253         Document signedDoc = builder.build(crypto);
254 
255         if (LOG.isDebugEnabled()) {
256             String outputString =
257                 XMLUtils.prettyDocumentToString(signedDoc);
258             LOG.debug(outputString);
259         }
260 
261         Element securityHeader = WSSecurityUtil.getSecurityHeader(signedDoc, null);
262         AlgorithmSuite algorithmSuite = createAlgorithmSuite();
263 
264         try {
265             verify(securityHeader, algorithmSuite, crypto);
266             fail("Expected failure as Digest algorithm is not allowed");
267         } catch (WSSecurityException ex) {
268             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
269         }
270 
271         algorithmSuite.addDigestAlgorithm(WSConstants.SHA256);
272         verify(securityHeader, algorithmSuite, crypto);
273     }
274 
275     private AlgorithmSuite createAlgorithmSuite() {
276         AlgorithmSuite algorithmSuite = new AlgorithmSuite();
277         algorithmSuite.addSignatureMethod(WSConstants.RSA_SHA1);
278         algorithmSuite.setMinimumAsymmetricKeyLength(512);
279         algorithmSuite.addC14nAlgorithm(WSConstants.C14N_EXCL_OMIT_COMMENTS);
280         algorithmSuite.addDigestAlgorithm(WSConstants.SHA1);
281 
282         return algorithmSuite;
283     }
284 
285     private WSHandlerResult verify(
286         Element securityHeader, AlgorithmSuite algorithmSuite, Crypto sigVerCrypto
287     ) throws Exception {
288         WSSecurityEngine secEngine = new WSSecurityEngine();
289         RequestData data = new RequestData();
290         data.setSigVerCrypto(sigVerCrypto);
291 
292         data.setAlgorithmSuite(algorithmSuite);
293 
294         List<BSPRule> ignoredRules = new ArrayList<>();
295         ignoredRules.add(BSPRule.R5404);
296         ignoredRules.add(BSPRule.R5406);
297         data.setIgnoredBSPRules(ignoredRules);
298 
299         return secEngine.processSecurityHeader(securityHeader, data);
300     }
301 
302 }