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  package org.apache.wss4j.policy.stax.assertionStates;
20  
21  import org.apache.wss4j.policy.AssertionState;
22  import org.apache.wss4j.policy.SPConstants;
23  import org.apache.wss4j.common.WSSPolicyException;
24  import org.apache.wss4j.policy.model.AbstractSecurityAssertion;
25  import org.apache.wss4j.policy.model.AbstractSymmetricAsymmetricBinding;
26  import org.apache.xml.security.exceptions.XMLSecurityException;
27  import org.apache.xml.security.stax.securityEvent.EncryptedElementSecurityEvent;
28  import org.apache.xml.security.stax.securityEvent.SecurityEvent;
29  import org.apache.xml.security.stax.securityEvent.SecurityEventConstants;
30  import org.apache.xml.security.stax.securityEvent.TokenSecurityEvent;
31  import org.apache.xml.security.stax.securityToken.SecurityToken;
32  import org.apache.wss4j.policy.stax.Assertable;
33  import org.apache.wss4j.policy.stax.DummyPolicyAsserter;
34  import org.apache.wss4j.policy.stax.PolicyAsserter;
35  import org.apache.wss4j.stax.ext.WSSConstants;
36  import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
37  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
38  import org.apache.wss4j.stax.utils.WSSUtils;
39  
40  import javax.xml.namespace.QName;
41  
42  import java.util.ArrayList;
43  import java.util.Iterator;
44  import java.util.LinkedList;
45  import java.util.List;
46  
47  /**
48   * WSP1.3, 6.4 Signature Protection Property
49   */
50  public class SignatureProtectionAssertionState extends AssertionState implements Assertable {
51  
52      private final List<EncryptedElementSecurityEvent> encryptedElementEvents = new ArrayList<>();
53      private final List<TokenSecurityEvent<? extends SecurityToken>> tokenSecurityEvents = new ArrayList<>();
54      private final List<List<QName>> elementPaths = new ArrayList<>();
55      private PolicyAsserter policyAsserter;
56  
57      public SignatureProtectionAssertionState(AbstractSecurityAssertion assertion,
58                                               PolicyAsserter policyAsserter,
59                                               boolean asserted) {
60          super(assertion, asserted);
61          List<QName> signature11Path = new LinkedList<>();
62          signature11Path.addAll(WSSConstants.SOAP_11_HEADER_PATH);
63          signature11Path.add(WSSConstants.TAG_WSSE_SECURITY);
64          signature11Path.add(WSSConstants.TAG_dsig_Signature);
65          elementPaths.add(signature11Path);
66  
67          List<QName> signatureConfirmation11Path = new LinkedList<>();
68          signatureConfirmation11Path.addAll(WSSConstants.SOAP_11_HEADER_PATH);
69          signatureConfirmation11Path.add(WSSConstants.TAG_WSSE_SECURITY);
70          signatureConfirmation11Path.add(WSSConstants.TAG_WSSE11_SIG_CONF);
71          elementPaths.add(signatureConfirmation11Path);
72  
73          this.policyAsserter = policyAsserter;
74          if (this.policyAsserter == null) {
75              this.policyAsserter = new DummyPolicyAsserter();
76          }
77  
78          if (asserted) {
79              String namespace = getAssertion().getName().getNamespaceURI();
80              policyAsserter.assertPolicy(new QName(namespace, SPConstants.ENCRYPT_SIGNATURE));
81          }
82      }
83  
84      @Override
85      public SecurityEventConstants.Event[] getSecurityEventType() {
86          return new SecurityEventConstants.Event[]{
87                  WSSecurityEventConstants.EncryptedElement,
88                  WSSecurityEventConstants.EncryptedKeyToken,
89                  WSSecurityEventConstants.ISSUED_TOKEN,
90                  WSSecurityEventConstants.KERBEROS_TOKEN,
91                  SecurityEventConstants.KeyValueToken,
92                  WSSecurityEventConstants.REL_TOKEN,
93                  WSSecurityEventConstants.SAML_TOKEN,
94                  WSSecurityEventConstants.SECURITY_CONTEXT_TOKEN,
95                  WSSecurityEventConstants.USERNAME_TOKEN,
96                  SecurityEventConstants.X509Token,
97                  WSSecurityEventConstants.OPERATION,
98          };
99      }
100 
101     @Override
102     public boolean assertEvent(SecurityEvent securityEvent) throws WSSPolicyException {
103         if (securityEvent instanceof EncryptedElementSecurityEvent) {
104             EncryptedElementSecurityEvent encryptedElementSecurityEvent =
105                 (EncryptedElementSecurityEvent) securityEvent;
106             // Store all matching Signature/SignatureConfirmation Elements
107             Iterator<List<QName>> pathElementsIterator = elementPaths.iterator();
108             while (pathElementsIterator.hasNext()) {
109                 List<QName> qNameList = pathElementsIterator.next();
110                 if (WSSUtils.pathMatches(qNameList, encryptedElementSecurityEvent.getElementPath())) {
111                     encryptedElementEvents.add(encryptedElementSecurityEvent);
112                 }
113             }
114         } else if (securityEvent instanceof TokenSecurityEvent) {
115             @SuppressWarnings("unchecked")
116             TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent
117                 = (TokenSecurityEvent<? extends SecurityToken>) securityEvent;
118             tokenSecurityEvents.add(tokenSecurityEvent);
119         }
120 
121         return true;
122     }
123 
124     @Override
125     public boolean isAsserted() {
126         clearErrorMessage();
127 
128         // If we only have one (main) Signature, then check that it matches the policy
129         if (encryptedElementEvents.size() == 1) {
130             return testEncryptedSignature(encryptedElementEvents.get(0));
131         } else if (encryptedElementEvents.size() > 1) {
132             // Otherwise we only check the policy for the main Signature
133             String endorsingSignatureId = findEndorsingSignatureId();
134             for (EncryptedElementSecurityEvent encryptedElementSecurityEvent : encryptedElementEvents) {
135                 String elementId = encryptedElementSecurityEvent.getCorrelationID();
136                 if (endorsingSignatureId != null && endorsingSignatureId.equals(elementId)) {
137                     // Skip this Endorsing Signature
138                     continue;
139                 }
140                 if (!testEncryptedSignature(encryptedElementSecurityEvent)) {
141                     return false;
142                 }
143             }
144         }
145 
146         return true;
147     }
148 
149     private String findEndorsingSignatureId() {
150         for (int i = 0; i < tokenSecurityEvents.size(); i++) {
151             TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent = tokenSecurityEvents.get(i);
152             try {
153                 SecurityToken securityToken =
154                     getEffectiveSignatureToken(tokenSecurityEvent.getSecurityToken());
155                 if (isSignatureToken(securityToken) && !isMainSignatureToken(securityToken)) {
156                     return tokenSecurityEvent.getCorrelationID();
157                 }
158             } catch (XMLSecurityException e) {
159                 // Just return null here
160                 return null;
161             }
162         }
163         return null;
164     }
165 
166     private boolean isSignatureToken(SecurityToken securityToken) {
167         List<WSSecurityTokenConstants.TokenUsage> tokenUsages = securityToken.getTokenUsages();
168         for (int i = 0; i < tokenUsages.size(); i++) {
169             WSSecurityTokenConstants.TokenUsage tokenUsage = tokenUsages.get(i);
170             if (WSSecurityTokenConstants.TokenUsage_Signature.equals(tokenUsage)
171                     || WSSecurityTokenConstants.TOKENUSAGE_MAIN_SIGNATURE.equals(tokenUsage)
172                     || tokenUsage.getName().contains("Endorsing")) {
173                 return true;
174             }
175         }
176         return false;
177     }
178 
179     private boolean isMainSignatureToken(SecurityToken securityToken) throws XMLSecurityException {
180         SecurityToken rootToken = WSSUtils.getRootToken(securityToken);
181         List<WSSecurityTokenConstants.TokenUsage> tokenUsages = rootToken.getTokenUsages();
182         return tokenUsages.contains(WSSecurityTokenConstants.TOKENUSAGE_MAIN_SIGNATURE);
183     }
184 
185     private SecurityToken getEffectiveSignatureToken(SecurityToken securityToken) throws XMLSecurityException {
186         SecurityToken tmp = WSSUtils.getRootToken(securityToken);
187         List<? extends SecurityToken> wrappedTokens = tmp.getWrappedTokens();
188         for (int i = 0; i < wrappedTokens.size(); i++) {
189             SecurityToken token = wrappedTokens.get(i);
190             if (isSignatureToken(token)) {
191                 //WSP 1.3, 6.5 [Token Protection] Property: Note that in cases where derived keys are used
192                 //the 'main' token, and NOT the derived key token, is covered by the signature.
193                 if (WSSecurityTokenConstants.DerivedKeyToken.equals(token.getTokenType())) {
194                     return tmp;
195                 }
196                 tmp = token;
197             }
198         }
199         return tmp;
200     }
201 
202     private boolean testEncryptedSignature(EncryptedElementSecurityEvent encryptedElementSecurityEvent) {
203         AbstractSymmetricAsymmetricBinding abstractSymmetricAsymmetricBinding =
204             (AbstractSymmetricAsymmetricBinding) getAssertion();
205 
206         String namespace = getAssertion().getName().getNamespaceURI();
207 
208         if (encryptedElementSecurityEvent.isEncrypted()) {
209             if (abstractSymmetricAsymmetricBinding.isEncryptSignature()) {
210                 setAsserted(true);
211                 policyAsserter.assertPolicy(new QName(namespace, SPConstants.ENCRYPT_SIGNATURE));
212                 return true;
213             } else {
214                 setAsserted(false);
215                 setErrorMessage("Element " + WSSUtils.pathAsString(encryptedElementSecurityEvent.getElementPath())
216                     + " must not be encrypted");
217                 policyAsserter.unassertPolicy(new QName(namespace, SPConstants.ENCRYPT_SIGNATURE),
218                                               getErrorMessage());
219                 return false;
220             }
221         } else {
222             if (abstractSymmetricAsymmetricBinding.isEncryptSignature()) {
223                 setAsserted(false);
224                 setErrorMessage("Element " + WSSUtils.pathAsString(encryptedElementSecurityEvent.getElementPath())
225                     + " must be encrypted");
226                 policyAsserter.unassertPolicy(new QName(namespace, SPConstants.ENCRYPT_SIGNATURE),
227                                             getErrorMessage());
228                 return false;
229             } else {
230                 setAsserted(true);
231                 policyAsserter.assertPolicy(new QName(namespace, SPConstants.ENCRYPT_SIGNATURE));
232                 return true;
233             }
234         }
235     }
236 
237 }