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.components.crypto;
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.dom.handler.RequestData;
30  import org.apache.wss4j.dom.handler.WSHandlerResult;
31  import org.apache.wss4j.common.bsp.BSPRule;
32  import org.apache.wss4j.common.crypto.CertificateStore;
33  import org.apache.wss4j.common.crypto.Crypto;
34  import org.apache.wss4j.common.crypto.CryptoFactory;
35  import org.apache.wss4j.common.crypto.CryptoType;
36  import org.apache.wss4j.common.crypto.Merlin;
37  import org.apache.wss4j.common.ext.WSSecurityException;
38  import org.apache.wss4j.common.util.XMLUtils;
39  import org.apache.wss4j.dom.message.WSSecHeader;
40  import org.apache.wss4j.dom.message.WSSecSignature;
41  
42  import org.junit.jupiter.api.Test;
43  import org.w3c.dom.Document;
44  
45  import java.security.cert.X509Certificate;
46  import java.util.Collections;
47  import java.util.List;
48  import java.util.Properties;
49  
50  import javax.security.auth.callback.CallbackHandler;
51  
52  import static org.junit.jupiter.api.Assertions.assertEquals;
53  import static org.junit.jupiter.api.Assertions.assertNotNull;
54  import static org.junit.jupiter.api.Assertions.assertThrows;
55  import static org.junit.jupiter.api.Assertions.assertTrue;
56  import static org.junit.jupiter.api.Assertions.fail;
57  
58  /**
59   * This is a test for the CertificateStore Crypto instance. This class does not know anything
60   * about Java KeyStores, but just wraps a list of trusted certificates.
61   */
62  public class CertificateStoreTest {
63      private static final org.slf4j.Logger LOG =
64          org.slf4j.LoggerFactory.getLogger(CertificateStoreTest.class);
65      private WSSecurityEngine secEngine = new WSSecurityEngine();
66      private Crypto senderCrypto = CryptoFactory.getInstance("wss40.properties");
67      private Crypto receiverCrypto;
68      private CallbackHandler keystoreCallbackHandler = new KeystoreCallbackHandler();
69  
70      public CertificateStoreTest() throws Exception {
71          CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
72          cryptoType.setAlias("wss40");
73          X509Certificate[] certs = senderCrypto.getX509Certificates(cryptoType);
74          receiverCrypto = new CertificateStore(certs);
75          WSSConfig.init();
76      }
77  
78      /**
79       * Test signing a SOAP message using a BST.
80       */
81      @Test
82      public void testSignatureDirectReference() throws Exception {
83          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
84          WSSecHeader secHeader = new WSSecHeader(doc);
85          secHeader.insertSecurityHeader();
86  
87          WSSecSignature sign = new WSSecSignature(secHeader);
88          sign.setUserInfo("wss40", "security");
89          sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
90  
91          Document signedDoc = sign.build(senderCrypto);
92  
93          if (LOG.isDebugEnabled()) {
94              String outputString =
95                  XMLUtils.prettyDocumentToString(signedDoc);
96              LOG.debug(outputString);
97          }
98          //
99          // Verify the signature
100         //
101         WSHandlerResult results = verify(signedDoc, receiverCrypto);
102         List<WSSecurityEngineResult> signatureResults =
103             results.getActionResults().get(WSConstants.SIGN);
104         WSSecurityEngineResult result = signatureResults.get(0);
105         X509Certificate cert =
106             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
107         assertNotNull(cert);
108     }
109 
110     /**
111      * Test signing a SOAP message using an X.509 Key Identifier.
112      */
113     @Test
114     public void testSignatureX509() throws Exception {
115         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
116         WSSecHeader secHeader = new WSSecHeader(doc);
117         secHeader.insertSecurityHeader();
118 
119         WSSecSignature sign = new WSSecSignature(secHeader);
120         sign.setUserInfo("wss40", "security");
121         sign.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
122 
123         Document signedDoc = sign.build(senderCrypto);
124 
125         if (LOG.isDebugEnabled()) {
126             String outputString =
127                 XMLUtils.prettyDocumentToString(signedDoc);
128             LOG.debug(outputString);
129         }
130         //
131         // Verify the signature
132         //
133         WSSecurityEngine newEngine = new WSSecurityEngine();
134         RequestData data = new RequestData();
135         data.setCallbackHandler(keystoreCallbackHandler);
136         data.setSigVerCrypto(receiverCrypto);
137         data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R3063));
138         WSHandlerResult results = newEngine.processSecurityHeader(signedDoc, data);
139 
140         List<WSSecurityEngineResult> signatureResults =
141             results.getActionResults().get(WSConstants.SIGN);
142         WSSecurityEngineResult result = signatureResults.get(0);
143         X509Certificate cert =
144             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
145         assertNotNull(cert);
146     }
147 
148     /**
149      * Test signing a SOAP message using Issuer Serial.
150      */
151     @Test
152     public void testSignatureIssuerSerial() throws Exception {
153         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
154         WSSecHeader secHeader = new WSSecHeader(doc);
155         secHeader.insertSecurityHeader();
156 
157         WSSecSignature sign = new WSSecSignature(secHeader);
158         sign.setUserInfo("wss40", "security");
159         sign.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
160 
161         Document signedDoc = sign.build(senderCrypto);
162 
163         if (LOG.isDebugEnabled()) {
164             String outputString =
165                 XMLUtils.prettyDocumentToString(signedDoc);
166             LOG.debug(outputString);
167         }
168 
169         //
170         // Verify the signature
171         //
172         WSHandlerResult results = verify(signedDoc, receiverCrypto);
173 
174         List<WSSecurityEngineResult> signatureResults =
175             results.getActionResults().get(WSConstants.SIGN);
176         WSSecurityEngineResult result = signatureResults.get(0);
177         X509Certificate cert =
178             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
179         assertNotNull(cert);
180     }
181 
182     /**
183      * Test signing a SOAP message using a Thumbprint
184      */
185     @Test
186     public void testSignatureThumbprint() throws Exception {
187         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
188         WSSecHeader secHeader = new WSSecHeader(doc);
189         secHeader.insertSecurityHeader();
190 
191         WSSecSignature sign = new WSSecSignature(secHeader);
192         sign.setUserInfo("wss40", "security");
193         sign.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
194 
195         Document signedDoc = sign.build(senderCrypto);
196 
197         if (LOG.isDebugEnabled()) {
198             String outputString =
199                 XMLUtils.prettyDocumentToString(signedDoc);
200             LOG.debug(outputString);
201         }
202 
203         //
204         // Verify the signature
205         //
206         WSHandlerResult results = verify(signedDoc, receiverCrypto);
207         List<WSSecurityEngineResult> signatureResults =
208             results.getActionResults().get(WSConstants.SIGN);
209         WSSecurityEngineResult result = signatureResults.get(0);
210 
211         X509Certificate cert =
212             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
213         assertNotNull(cert);
214     }
215 
216     /**
217      * Test signing a SOAP message using a SKI Key Identifier
218      */
219     @Test
220     public void testSignatureSKI() throws Exception {
221         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
222         WSSecHeader secHeader = new WSSecHeader(doc);
223         secHeader.insertSecurityHeader();
224 
225         WSSecSignature sign = new WSSecSignature(secHeader);
226         sign.setUserInfo("wss40", "security");
227         sign.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER);
228 
229         Document signedDoc = sign.build(senderCrypto);
230 
231         if (LOG.isDebugEnabled()) {
232             String outputString =
233                 XMLUtils.prettyDocumentToString(signedDoc);
234             LOG.debug(outputString);
235         }
236 
237         //
238         // Verify the signature
239         //
240         WSHandlerResult results = verify(signedDoc, receiverCrypto);
241 
242         List<WSSecurityEngineResult> signatureResults =
243             results.getActionResults().get(WSConstants.SIGN);
244         WSSecurityEngineResult result = signatureResults.get(0);
245         X509Certificate cert =
246             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
247         assertNotNull(cert);
248     }
249 
250     /**
251      * Test signing a SOAP message using a BST. The certificate is not known to the
252      * CertificateStore and so should throw an exception.
253      */
254     @Test
255     public void testSignatureDirectReferenceUntrusted() throws Exception {
256         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
257         WSSecHeader secHeader = new WSSecHeader(doc);
258         secHeader.insertSecurityHeader();
259 
260         WSSecSignature sign = new WSSecSignature(secHeader);
261         sign.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
262         sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
263 
264         Document signedDoc = sign.build(CryptoFactory.getInstance());
265 
266         if (LOG.isDebugEnabled()) {
267             String outputString =
268                 XMLUtils.prettyDocumentToString(signedDoc);
269             LOG.debug(outputString);
270         }
271         //
272         // Verify the signature
273         //
274         try {
275             verify(signedDoc, receiverCrypto);
276             fail("Failure expected on an unknown certificate");
277         } catch (WSSecurityException ex) {
278             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILURE);
279         }
280     }
281 
282     @Test
283     public void testAliasNotAllowed() throws Exception {
284         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
285         cryptoType.setAlias("wss40");
286         assertThrows(WSSecurityException.class, () -> {
287             receiverCrypto.getX509Certificates(cryptoType);
288         });
289     }
290 
291     /**
292      * Verifies the soap envelope
293      * <p/>
294      *
295      * @param doc
296      * @throws Exception Thrown when there is a problem in verification
297      */
298     private WSHandlerResult verify(Document doc, Crypto crypto) throws Exception {
299         WSHandlerResult results = secEngine.processSecurityHeader(
300             doc, null, keystoreCallbackHandler, crypto
301         );
302         if (LOG.isDebugEnabled()) {
303             LOG.debug("Verfied and decrypted message:");
304             String outputString =
305                 XMLUtils.prettyDocumentToString(doc);
306             LOG.debug(outputString);
307         }
308         return results;
309     }
310 
311 
312 
313     /**
314      * A certificate that the CertificateStore holds directly is still only trusted while it is
315      * within its validity period. Every other path through verifyTrust runs the certificate
316      * through a CertPathValidator, which enforces that; the direct-trust shortcut has to enforce
317      * it itself.
318      */
319     @Test
320     public void testExpiredCertificateIsNotDirectlyTrusted() throws Exception {
321         Properties properties = new Properties();
322         properties.put("org.apache.wss4j.crypto.provider",
323                        "org.apache.wss4j.common.crypto.Merlin");
324         properties.put("org.apache.wss4j.crypto.merlin.keystore.type", "jks");
325         properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "security");
326         properties.put("org.apache.wss4j.crypto.merlin.keystore.alias", "wss40exp");
327         properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "keys/wss40exp.jks");
328         Crypto expiredCrypto = new Merlin(properties, this.getClass().getClassLoader(), null);
329 
330         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
331         cryptoType.setAlias("wss40exp");
332         X509Certificate[] certChain = expiredCrypto.getX509Certificates(cryptoType);
333         assertNotNull(certChain);
334         // Only the leaf, so that the direct-trust shortcut in verifyTrust is the path taken
335         X509Certificate[] expiredCerts = new X509Certificate[] {certChain[0]};
336 
337         Crypto certificateStore = new CertificateStore(expiredCerts);
338 
339         WSSecurityException ex =
340             assertThrows(WSSecurityException.class,
341                 () -> certificateStore.verifyTrust(expiredCerts, false, null, null));
342         assertEquals(WSSecurityException.ErrorCode.FAILED_CHECK, ex.getErrorCode());
343     }
344 
345 }