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.stax.impl.securityToken;
20  
21  import java.io.IOException;
22  import java.security.Key;
23  import java.security.Principal;
24  import java.security.PrivateKey;
25  import java.security.PublicKey;
26  import java.security.cert.X509Certificate;
27  import java.util.Collection;
28  import java.util.List;
29  import java.util.regex.Pattern;
30  
31  import javax.crypto.spec.SecretKeySpec;
32  import javax.security.auth.Subject;
33  import javax.security.auth.callback.Callback;
34  import javax.security.auth.callback.UnsupportedCallbackException;
35  
36  import org.w3c.dom.Element;
37  import org.apache.wss4j.common.crypto.Crypto;
38  import org.apache.wss4j.common.ext.WSPasswordCallback;
39  import org.apache.wss4j.common.ext.WSSecurityException;
40  import org.apache.wss4j.common.principal.SAMLTokenPrincipal;
41  import org.apache.wss4j.common.saml.OpenSAMLUtil;
42  import org.apache.wss4j.common.saml.SAMLKeyInfo;
43  import org.apache.wss4j.common.saml.SAMLUtil;
44  import org.apache.wss4j.common.saml.SamlAssertionWrapper;
45  import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
46  import org.apache.wss4j.stax.ext.WSSConstants;
47  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
48  import org.apache.wss4j.stax.securityToken.SamlSecurityToken;
49  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
50  import org.apache.xml.security.algorithms.JCEMapper;
51  import org.apache.xml.security.exceptions.XMLSecurityException;
52  import org.apache.xml.security.stax.ext.XMLSecurityConstants;
53  import org.apache.xml.security.stax.impl.securityToken.AbstractInboundSecurityToken;
54  import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
55  import org.opensaml.saml.common.SAMLVersion;
56  
57  public class SamlSecurityTokenImpl extends AbstractInboundSecurityToken implements SamlSecurityToken {
58  
59      private final SamlAssertionWrapper samlAssertionWrapper;
60      private InboundSecurityToken subjectSecurityToken;
61      private Crypto crypto;
62      private WSSSecurityProperties securityProperties;
63      private Principal principal;
64      private SAMLKeyInfo subjectKeyInfo;
65      private byte[] secret;
66      private Key key;
67  
68      public SamlSecurityTokenImpl(WSInboundSecurityContext wsInboundSecurityContext, String id,
69                                   WSSecurityTokenConstants.KeyIdentifier keyIdentifier,
70                                   WSSSecurityProperties securityProperties) throws WSSecurityException {
71          super(wsInboundSecurityContext, id, keyIdentifier, false);
72          this.securityProperties = securityProperties;
73          if (securityProperties.getCallbackHandler() != null) {
74              // Try to get the Assertion from a CallbackHandler
75              WSPasswordCallback pwcb =
76                  new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
77              try {
78                  securityProperties.getCallbackHandler().handle(new Callback[]{pwcb});
79              } catch (IOException | UnsupportedCallbackException e) {
80                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "noPassword");
81              }
82  
83              secret = pwcb.getKey();
84              key = pwcb.getKeyObject();
85              if (this.key instanceof PrivateKey) {
86                  super.setAsymmetric(true);
87              }
88  
89              Element assertionElem = pwcb.getCustomToken();
90              if (assertionElem != null && "Assertion".equals(assertionElem.getLocalName())
91                  && (WSSConstants.NS_SAML.equals(assertionElem.getNamespaceURI())
92                  || WSSConstants.NS_SAML2.equals(assertionElem.getNamespaceURI()))) {
93                  this.samlAssertionWrapper = new SamlAssertionWrapper(assertionElem);
94  
95                  subjectKeyInfo =
96                      SAMLUtil.getCredentialFromSubject(samlAssertionWrapper, null,
97                                                        securityProperties.getSignatureVerificationCrypto());
98              } else {
99                  // Possibly an Encrypted Assertion...we just need the key here
100                 this.samlAssertionWrapper = null;
101             }
102 
103             if (this.samlAssertionWrapper == null && secret == null && key == null) {
104                 throw new WSSecurityException(
105                     WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, "noToken",
106                     new Object[] {id}
107                 );
108             }
109         } else {
110             throw new WSSecurityException(
111                 WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, "noToken",
112                 new Object[] {id}
113             );
114         }
115     }
116 
117     public SamlSecurityTokenImpl(SamlAssertionWrapper samlAssertionWrapper, InboundSecurityToken subjectSecurityToken,
118                                  WSInboundSecurityContext wsInboundSecurityContext, Crypto crypto,
119                                  WSSecurityTokenConstants.KeyIdentifier keyIdentifier,
120                                  WSSSecurityProperties securityProperties) {
121         super(wsInboundSecurityContext, samlAssertionWrapper.getId(), keyIdentifier, true);
122         this.samlAssertionWrapper = samlAssertionWrapper;
123         this.crypto = crypto;
124         this.subjectSecurityToken = subjectSecurityToken;
125         this.securityProperties = securityProperties;
126     }
127 
128     @Override
129     public boolean isAsymmetric() throws XMLSecurityException {
130         if (this.subjectSecurityToken != null && this.subjectSecurityToken.isAsymmetric()) {
131             return true;
132         }
133         return super.isAsymmetric();
134     }
135 
136     @Override
137     protected Key getKey(String algorithmURI, XMLSecurityConstants.AlgorithmUsage algorithmUsage, String correlationID)
138         throws XMLSecurityException {
139         Key key = null;
140         if (this.key != null) {
141             key = this.key;
142         } else if (secret != null) {
143             String keyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
144             key = new SecretKeySpec(secret, keyAlgorithm);
145         } else if (this.subjectSecurityToken != null) {
146             key = subjectSecurityToken.getSecretKey(algorithmURI, algorithmUsage, correlationID);
147         } else if (subjectKeyInfo != null && subjectKeyInfo.getSecret() != null) {
148             String keyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
149             key = new SecretKeySpec(subjectKeyInfo.getSecret(), keyAlgorithm);
150         }
151         if (key != null) {
152             super.setSecretKey(algorithmURI, key);
153             return key;
154         }
155         return super.getKey(algorithmURI, algorithmUsage, correlationID);
156     }
157 
158     @Override
159     protected PublicKey getPubKey(String algorithmURI, XMLSecurityConstants.AlgorithmUsage algorithmUsage, String correlationID)
160         throws XMLSecurityException {
161         if (this.subjectSecurityToken != null) {
162             return subjectSecurityToken.getPublicKey(algorithmURI, algorithmUsage, correlationID);
163         } else if (subjectKeyInfo != null && subjectKeyInfo.getPublicKey() != null) {
164             return subjectKeyInfo.getPublicKey();
165         }
166         return super.getPubKey(algorithmURI, algorithmUsage, correlationID);
167     }
168 
169     @Override
170     public PublicKey getPublicKey() throws XMLSecurityException {
171         if (this.subjectSecurityToken != null) {
172             return subjectSecurityToken.getPublicKey();
173         } else if (subjectKeyInfo != null && subjectKeyInfo.getPublicKey() != null) {
174             return subjectKeyInfo.getPublicKey();
175         }
176         return super.getPublicKey();
177     }
178 
179     @Override
180     public X509Certificate[] getX509Certificates() throws XMLSecurityException {
181         if (this.subjectSecurityToken != null) {
182             return subjectSecurityToken.getX509Certificates();
183         } else if (subjectKeyInfo != null && subjectKeyInfo.getCerts() != null) {
184             return subjectKeyInfo.getCerts();
185         }
186         return super.getX509Certificates();
187     }
188 
189     @Override
190     public void verify() throws XMLSecurityException {
191         //todo revisit verify for every security token incl. public-key
192         //todo should we call verify implicit when accessing the keys?
193         if (samlAssertionWrapper == null) {
194             return;
195         }
196         String confirmMethod = null;
197         List<String> methods = samlAssertionWrapper.getConfirmationMethods();
198         if (methods != null && !methods.isEmpty()) {
199             confirmMethod = methods.get(0);
200         }
201         // If HOK + Token is signed then we don't need to verify the subject cert, as we
202         // indirectly trust it
203         if (!OpenSAMLUtil.isMethodHolderOfKey(confirmMethod) && !samlAssertionWrapper.isSigned()) {
204             X509Certificate[] x509Certificates = getX509Certificates();
205             if (x509Certificates != null && x509Certificates.length > 0) {
206                 boolean enableRevocation = false;
207                 Collection<Pattern> subjectCertConstraints = null;
208                 Collection<Pattern> issuerCertConstraints = null;
209                 if (securityProperties != null) {
210                     enableRevocation = securityProperties.isEnableRevocation();
211                     subjectCertConstraints = securityProperties.getSubjectCertConstraints();
212                     issuerCertConstraints = securityProperties.getIssuerDNConstraints();
213 
214                 }
215                 crypto.verifyTrust(x509Certificates, enableRevocation, subjectCertConstraints, issuerCertConstraints);
216             }
217             PublicKey publicKey = getPublicKey();
218             if (publicKey != null) {
219                 crypto.verifyTrust(publicKey);
220             }
221         }
222     }
223 
224     @Override
225     public WSSecurityTokenConstants.TokenType getTokenType() {
226         if (samlAssertionWrapper != null
227             && samlAssertionWrapper.getSamlVersion() == SAMLVersion.VERSION_10) {
228             return WSSecurityTokenConstants.SAML_10_TOKEN;
229         } else if (samlAssertionWrapper != null
230             && samlAssertionWrapper.getSamlVersion() == SAMLVersion.VERSION_11) {
231             return WSSecurityTokenConstants.SAML_11_TOKEN;
232         }
233         return WSSecurityTokenConstants.SAML_20_TOKEN;
234     }
235 
236     @Override
237     public Subject getSubject() throws WSSecurityException {
238         return null;
239     }
240 
241     @Override
242     public Principal getPrincipal() throws WSSecurityException {
243         if (this.principal == null) {
244             this.principal = new SAMLTokenPrincipal() {
245                 @Override
246                 public SamlAssertionWrapper getToken() {
247                     return samlAssertionWrapper;
248                 }
249 
250                 @Override
251                 public String getName() {
252                     return samlAssertionWrapper.getSubjectName();
253                 }
254 
255                 @Override
256                 public String getId() {
257                     return samlAssertionWrapper.getId();
258                 }
259             };
260         }
261         return this.principal;
262     }
263 
264     @Override
265     public SamlAssertionWrapper getSamlAssertionWrapper() {
266         return samlAssertionWrapper;
267     }
268 }