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;
21  
22  import org.apache.ws.security.WSSConfig;
23  import org.apache.ws.security.WSSecurityEngine;
24  import org.apache.ws.security.WSConstants;
25  import org.apache.ws.security.WSSecurityEngineResult;
26  import org.apache.ws.security.WSSecurityException;
27  import org.apache.ws.security.common.CustomHandler;
28  import org.apache.ws.security.common.SOAPUtil;
29  import org.apache.ws.security.components.crypto.Crypto;
30  import org.apache.ws.security.components.crypto.CryptoFactory;
31  import org.apache.ws.security.handler.RequestData;
32  import org.apache.ws.security.handler.WSHandlerConstants;
33  import org.apache.ws.security.util.WSSecurityUtil;
34  import org.w3c.dom.Document;
35  
36  import java.security.cert.X509Certificate;
37  import java.util.List;
38  
39  /**
40   * This is a test for WSS-40. Essentially it just tests that a message is signed using a
41   * keyEntry from one keystore, and verified at the other end with a keystore with just the
42   * CA cert in it.
43   * 
44   * http://issues.apache.org/jira/browse/WSS-40
45   * 
46   * Generate the CA keys/certs + export the CA cert to a keystore
47   * 
48   * openssl req -x509 -newkey rsa:1024 -keyout wss40CAKey.pem -out wss40CA.pem 
49   * -config ca.config -days 3650
50   * openssl x509 -outform DER -in wss40CA.pem -out wss40CA.crt
51   * keytool -import -file wss40CA.crt -alias wss40CA -keystore wss40CA.jks
52   * 
53   * Generate the client keypair, make a csr, sign it with the CA key
54   * 
55   * keytool -genkey -validity 3650 -alias wss40 -keyalg RSA -keystore wss40.jks 
56   * -dname "CN=Colm,OU=WSS4J,O=Apache,L=Dublin,ST=Leinster,C=IE"
57   * keytool -certreq -alias wss40 -keystore wss40.jks -file wss40.cer
58   * openssl ca -config ca.config -policy policy_anything -days 3650 -out wss40.pem 
59   * -infiles wss40.cer
60   * openssl x509 -outform DER -in wss40.pem -out wss40.crt
61   * 
62   * Import the CA cert into wss40.jks and import the new signed certificate
63   * 
64   * keytool -import -file wss40CA.crt -alias wss40CA -keystore wss40.jks
65   * keytool -import -file wss40.crt -alias wss40 -keystore wss40.jks
66   * 
67   */
68  public class SignatureCertTest extends org.junit.Assert {
69      private static final org.apache.commons.logging.Log LOG = 
70          org.apache.commons.logging.LogFactory.getLog(SignatureCertTest.class);
71      private WSSecurityEngine secEngine = new WSSecurityEngine();
72      private Crypto crypto = null;
73      private Crypto cryptoCA = null;
74      
75      public SignatureCertTest() throws Exception {
76          WSSConfig.init();
77          crypto = CryptoFactory.getInstance("wss40.properties");
78          cryptoCA = CryptoFactory.getInstance("wss40CA.properties");
79      }
80  
81      /**
82       * Test signing a SOAP message using a BST.
83       */
84      @org.junit.Test
85      public void testSignatureDirectReference() throws Exception {
86          WSSecSignature sign = new WSSecSignature();
87          sign.setUserInfo("wss40", "security");
88          sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
89  
90          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
91  
92          WSSecHeader secHeader = new WSSecHeader();
93          secHeader.insertSecurityHeader(doc);
94          Document signedDoc = sign.build(doc, crypto, secHeader);
95          
96          if (LOG.isDebugEnabled()) {
97              String outputString = 
98                  org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
99              LOG.debug(outputString);
100         }
101         //
102         // Verify the signature
103         //
104         List<WSSecurityEngineResult> results = verify(signedDoc, cryptoCA);
105         WSSecurityEngineResult result = 
106             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
107         X509Certificate cert = 
108             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
109         assertTrue (cert != null);
110     }
111     
112     // TODO disabling this test as the certs are expired
113     @org.junit.Test
114     @org.junit.Ignore
115     public void testBSTCertChain() throws Exception {
116         //
117         // This test fails with the IBM JDK
118         //
119         if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
120             return;
121         }
122         Crypto clientCrypto = CryptoFactory.getInstance("wss40_client.properties");
123         WSSecSignature sign = new WSSecSignature();
124         sign.setUserInfo("Client_CertChain", "password");
125         sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
126         sign.setUseSingleCertificate(false);
127        
128         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
129         
130         WSSecHeader secHeader = new WSSecHeader();
131         secHeader.insertSecurityHeader(doc);
132         Document signedDoc = sign.build(doc, clientCrypto, secHeader);
133                 
134         if (LOG.isDebugEnabled()) {
135             String outputString = 
136                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
137             LOG.debug("BST CA Cert");
138             LOG.debug(outputString);
139         }
140         //
141         // Verify the signature
142         //
143         Crypto serverCrypto = CryptoFactory.getInstance("wss40_server.properties");
144         List<WSSecurityEngineResult> results = verify(signedDoc, serverCrypto);
145         WSSecurityEngineResult result = 
146             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
147         X509Certificate cert = 
148             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
149         assertTrue (cert != null);
150         X509Certificate[] certs = 
151             (X509Certificate[])result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
152         assertTrue (certs != null && certs.length == 2);
153     }
154     
155     /**
156      * Test signing a SOAP message using a BST, sending the CA cert as well in the
157      * message.
158      */
159     @org.junit.Test
160     public void testSignatureDirectReferenceCACert() throws Exception {
161         WSSecSignature sign = new WSSecSignature();
162         sign.setUserInfo("wss40", "security");
163         sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
164         sign.setUseSingleCertificate(false);
165 
166         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
167 
168         WSSecHeader secHeader = new WSSecHeader();
169         secHeader.insertSecurityHeader(doc);
170         Document signedDoc = sign.build(doc, crypto, secHeader);
171         
172         if (LOG.isDebugEnabled()) {
173             String outputString = 
174                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
175             LOG.debug("BST CA Cert");
176             LOG.debug(outputString);
177         }
178         //
179         // Verify the signature
180         //
181         List<WSSecurityEngineResult> results = verify(signedDoc, cryptoCA);
182         WSSecurityEngineResult result = 
183             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
184         X509Certificate cert = 
185             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
186         assertTrue (cert != null);
187         X509Certificate[] certs = 
188             (X509Certificate[])result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
189         assertTrue (certs != null && certs.length == 2);
190     }
191     
192     
193     /**
194      * Test signing a SOAP message using Issuer Serial. Note that this should fail, as the
195      * trust-store does not contain the cert corresponding to wss40, only the CA cert
196      * wss40CA.
197      */
198     @org.junit.Test
199     public void testSignatureIssuerSerial() throws Exception {
200         WSSecSignature sign = new WSSecSignature();
201         sign.setUserInfo("wss40", "security");
202         sign.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
203 
204         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
205 
206         WSSecHeader secHeader = new WSSecHeader();
207         secHeader.insertSecurityHeader(doc);
208         Document signedDoc = sign.build(doc, crypto, secHeader);
209         
210         if (LOG.isDebugEnabled()) {
211             String outputString = 
212                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
213             LOG.debug(outputString);
214         }
215         
216         try {
217             verify(signedDoc, cryptoCA);
218             fail("Failure expected on issuer serial");
219         } catch (WSSecurityException ex) {
220             assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_CHECK);
221             // expected
222         }
223     }
224     
225     
226     /**
227      * Test signing a SOAP message using a BST. The signature verification passes, but the trust
228      * verification will fail as the CA cert is out of date.
229      */
230     @org.junit.Test
231     public void testSignatureBadCACert() throws Exception {
232         WSSecSignature sign = new WSSecSignature();
233         sign.setUserInfo("wss4jcertdsa", "security");
234         sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
235 
236         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
237 
238         WSSecHeader secHeader = new WSSecHeader();
239         secHeader.insertSecurityHeader(doc);
240         Document signedDoc = 
241             sign.build(doc, CryptoFactory.getInstance("wss40badca.properties"), secHeader);
242         
243         if (LOG.isDebugEnabled()) {
244             String outputString = 
245                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
246             LOG.debug(outputString);
247         }
248         //
249         // Verify the signature
250         //
251         try {
252             verify(signedDoc, CryptoFactory.getInstance("wss40badcatrust.properties"));
253             fail("Failure expected on bad CA cert!");
254         } catch (WSSecurityException ex) {
255             // expected
256         }
257     }
258     
259     /**
260      * A test for "SignatureAction does not set DigestAlgorithm on WSSecSignature instance"
261      */
262     @org.junit.Test
263     public void testMultipleCertsWSHandler() throws Exception {
264         final WSSConfig cfg = WSSConfig.getNewInstance();
265         final int action = WSConstants.SIGN;
266         final RequestData reqData = new RequestData();
267         reqData.setWssConfig(cfg);
268         reqData.setUsername("wss40");
269         java.util.Map<String, String> config = new java.util.TreeMap<String, String>();
270         config.put(WSHandlerConstants.SIG_PROP_FILE, "wss40.properties");
271         config.put("password", "security");
272         config.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
273         config.put(WSHandlerConstants.USE_SINGLE_CERTIFICATE, "false");
274         reqData.setMsgContext(config);
275         
276         final java.util.List<Integer> actions = new java.util.ArrayList<Integer>();
277         actions.add(Integer.valueOf(action));
278         final Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
279         CustomHandler handler = new CustomHandler();
280         handler.send(
281             action, 
282             doc, 
283             reqData, 
284             actions,
285             true
286         );
287         
288         //
289         // Verify the signature
290         //
291         List<WSSecurityEngineResult> results = verify(doc, cryptoCA);
292         WSSecurityEngineResult result = 
293             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
294         X509Certificate cert = 
295             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
296         assertTrue (cert != null);
297         X509Certificate[] certs = 
298             (X509Certificate[])result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
299         assertTrue (certs != null && certs.length == 2);
300     }
301     
302     
303     /**
304      * Verifies the soap envelope
305      * <p/>
306      * 
307      * @param doc 
308      * @throws Exception Thrown when there is a problem in verification
309      */
310     private List<WSSecurityEngineResult> 
311     verify(Document doc, Crypto crypto) throws WSSecurityException {
312         List<WSSecurityEngineResult> results = secEngine.processSecurityHeader(
313             doc, null, null, crypto
314         );
315         if (LOG.isDebugEnabled()) {
316             LOG.debug("Verfied and decrypted message:");
317             String outputString = 
318                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
319             LOG.debug(outputString);
320         }
321         return results;
322     }
323 
324     
325 }