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.SOAPUtil;
28  import org.apache.ws.security.components.crypto.Crypto;
29  import org.apache.ws.security.components.crypto.CryptoFactory;
30  import org.apache.ws.security.handler.RequestData;
31  import org.apache.ws.security.util.WSSecurityUtil;
32  import org.w3c.dom.Document;
33  import org.w3c.dom.Element;
34  
35  import java.security.cert.X509Certificate;
36  import java.util.List;
37  
38  /**
39   * This is a test for Certificate Revocation List checking. A message is signed and sent to the
40   * receiver. If Certificate Revocation is enabled, then signature trust verification should
41   * fail as the message has been signed by the private key corresponding to a revoked signature. 
42   * 
43   * Generate the client keypair, make a csr, sign it with the CA key
44   * 
45   * keytool -genkey -validity 3650 -alias wss40rev -keyalg RSA -keystore wss40rev.jks 
46   * -dname "CN=Colm,OU=WSS4J,O=Apache,L=Dublin,ST=Leinster,C=IE"
47   * keytool -certreq -alias wss40rev -keystore wss40rev.jks -file wss40rev.cer
48   * openssl ca -config ca.config -policy policy_anything -days 3650 -out wss40rev.pem 
49   * -infiles wss40rev.cer
50   * openssl x509 -outform DER -in wss40rev.pem -out wss40rev.crt
51   * 
52   * Import the CA cert into wss40.jks and import the new signed certificate
53   * 
54   * keytool -import -file wss40CA.crt -alias wss40CA -keystore wss40rev.jks
55   * keytool -import -file wss40rev.crt -alias wss40rev -keystore wss40rev.jks
56   * 
57   * Generate a Revocation list
58   * 
59   * openssl ca -gencrl -keyfile wss40CAKey.pem -cert wss40CA.pem -out wss40CACRL.pem 
60   * -config ca.config -crldays 3650
61   * openssl ca -revoke wss40rev.pem -keyfile wss40CAKey.pem -cert wss40CA.pem -config ca.config
62   * openssl ca -gencrl -keyfile wss40CAKey.pem -cert wss40CA.pem -out wss40CACRL.pem 
63   * -config ca.config -crldays 3650
64   */
65  public class SignatureCRLTest extends org.junit.Assert {
66      private static final org.apache.commons.logging.Log LOG = 
67          org.apache.commons.logging.LogFactory.getLog(SignatureCRLTest.class);
68      private Crypto crypto = null;
69      private Crypto cryptoCA = null;
70      
71      public SignatureCRLTest() throws Exception {
72          WSSConfig.init();
73          crypto = CryptoFactory.getInstance("wss40rev.properties");
74          cryptoCA = CryptoFactory.getInstance("wss40CA.properties");
75      }
76  
77      /**
78       * Test signing a SOAP message using a BST. Revocation is not enabled and so the test
79       * should pass.
80       */
81      @org.junit.Test
82      public void testSignatureDirectReference() throws Exception {
83          WSSecSignature sign = new WSSecSignature();
84          sign.setUserInfo("wss40rev", "security");
85          sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
86  
87          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
88  
89          WSSecHeader secHeader = new WSSecHeader();
90          secHeader.insertSecurityHeader(doc);
91          Document signedDoc = sign.build(doc, crypto, secHeader);
92          
93          if (LOG.isDebugEnabled()) {
94              String outputString = 
95                  org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
96              LOG.debug(outputString);
97          }
98          //
99          // Verify the signature
100         //
101         List<WSSecurityEngineResult> results = verify(signedDoc, cryptoCA, false);
102         WSSecurityEngineResult result = 
103             WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
104         X509Certificate cert = 
105             (X509Certificate)result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
106         assertTrue (cert != null);
107     }
108     
109     /**
110      * Test signing a SOAP message using a BST. Revocation is enabled and so the test
111      * should fail.
112      */
113     @org.junit.Test
114     public void testSignatureDirectReferenceRevocation() throws Exception {
115         WSSecSignature sign = new WSSecSignature();
116         sign.setUserInfo("wss40rev", "security");
117         sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
118 
119         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
120 
121         WSSecHeader secHeader = new WSSecHeader();
122         secHeader.insertSecurityHeader(doc);
123         Document signedDoc = sign.build(doc, crypto, secHeader);
124         
125         if (LOG.isDebugEnabled()) {
126             String outputString = 
127                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
128             LOG.debug(outputString);
129         }
130         //
131         // Verify the signature
132         //
133         try {
134             verify(signedDoc, cryptoCA, true);
135             fail ("Failure expected on a revoked certificate");
136         } catch (Exception ex) {
137             String errorMessage = ex.getMessage();
138             // Different errors using different JDKs...
139             assertTrue(errorMessage.contains("Certificate has been revoked")
140                 || errorMessage.contains("Certificate revocation")
141                 || errorMessage.contains("Error during certificate path validation"));
142         }
143     }
144     
145     
146     /**
147      * Test signing a SOAP message using a BST. Revocation is enabled and so the test
148      * should fail. The trust store that is used is the keystore that contains the revoked
149      * certificate. See WSS-341:
150      * https://issues.apache.org/jira/browse/WSS-341
151      */
152     @org.junit.Test
153     public void testSignatureDirectReferenceRevocationKeyStore() throws Exception {
154         WSSecSignature sign = new WSSecSignature();
155         sign.setUserInfo("wss40rev", "security");
156         sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
157 
158         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
159 
160         WSSecHeader secHeader = new WSSecHeader();
161         secHeader.insertSecurityHeader(doc);
162         Document signedDoc = sign.build(doc, crypto, secHeader);
163         
164         if (LOG.isDebugEnabled()) {
165             String outputString = 
166                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
167             LOG.debug(outputString);
168         }
169         //
170         // Verify the signature
171         //
172         try {
173             verify(signedDoc, crypto, true);
174             fail ("Failure expected on a revoked certificate");
175         } catch (Exception ex) {
176             String errorMessage = ex.getMessage();
177             // Different errors using different JDKs...
178             assertTrue(errorMessage.contains("Certificate has been revoked")
179                 || errorMessage.contains("Certificate revocation")
180                 || errorMessage.contains("Error during certificate path validation"));
181         }
182     }
183     
184     /**
185      * Verifies the soap envelope
186      * <p/>
187      * 
188      * @param doc 
189      * @throws Exception Thrown when there is a problem in verification
190      */
191     private List<WSSecurityEngineResult> 
192     verify(Document doc, Crypto crypto, boolean revocationEnabled) throws WSSecurityException {
193         WSSecurityEngine secEngine = new WSSecurityEngine();
194         RequestData reqData = new RequestData();
195         reqData.setSigCrypto(crypto);
196         reqData.setEnableRevocation(revocationEnabled);
197         Element securityHeader = WSSecurityUtil.getSecurityHeader(doc, null);
198         List<WSSecurityEngineResult> results = 
199             secEngine.processSecurityHeader(securityHeader, reqData);
200         if (LOG.isDebugEnabled()) {
201             LOG.debug("Verfied and decrypted message:");
202             String outputString = 
203                 org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
204             LOG.debug(outputString);
205         }
206         return results;
207     }
208 
209     
210 }