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.str;
21  
22  import java.security.Principal;
23  import java.security.cert.CertificateEncodingException;
24  import java.security.cert.X509Certificate;
25  import java.util.Arrays;
26  import java.util.List;
27  
28  import javax.xml.namespace.QName;
29  
30  import org.apache.wss4j.common.crypto.Crypto;
31  import org.apache.wss4j.common.ext.WSPasswordCallback;
32  import org.apache.wss4j.common.ext.WSSecurityException;
33  import org.apache.wss4j.common.principal.CustomTokenPrincipal;
34  import org.apache.wss4j.common.principal.SAMLTokenPrincipalImpl;
35  import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal;
36  import org.apache.wss4j.common.saml.OpenSAMLUtil;
37  import org.apache.wss4j.common.saml.SAMLKeyInfo;
38  import org.apache.wss4j.common.saml.SAMLUtil;
39  import org.apache.wss4j.common.saml.SamlAssertionWrapper;
40  import org.apache.wss4j.common.token.BinarySecurity;
41  import org.apache.wss4j.common.token.Reference;
42  import org.apache.wss4j.common.token.SecurityTokenReference;
43  import org.apache.wss4j.common.util.KeyUtils;
44  import org.apache.wss4j.common.util.XMLUtils;
45  import org.apache.wss4j.dom.WSConstants;
46  import org.apache.wss4j.dom.WSDocInfo;
47  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
48  import org.apache.wss4j.dom.handler.RequestData;
49  import org.apache.wss4j.dom.message.token.DerivedKeyToken;
50  import org.apache.wss4j.dom.message.token.SecurityContextToken;
51  import org.apache.wss4j.dom.message.token.UsernameToken;
52  import org.apache.wss4j.dom.processor.Processor;
53  import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor;
54  import org.w3c.dom.Element;
55  
56  /**
57   * This implementation of STRParser is for parsing a SecurityTokenReference element, found in the
58   * KeyInfo element associated with a Signature element.
59   */
60  public class SignatureSTRParser implements STRParser {
61  
62      /**
63       * Parse a SecurityTokenReference element and extract credentials.
64       *
65       * @param parameters The parameters to parse
66       * @return the STRParserResult Object containing the parsing results
67       * @throws WSSecurityException
68       */
69      public STRParserResult parseSecurityTokenReference(STRParserParameters parameters) throws WSSecurityException {
70  
71          if (parameters == null || parameters.getData() == null || parameters.getData().getWsDocInfo() == null
72              || parameters.getStrElement() == null) {
73              throw new WSSecurityException(
74                  WSSecurityException.ErrorCode.FAILURE, "invalidSTRParserParameter"
75              );
76          }
77  
78          SecurityTokenReference secRef =
79              new SecurityTokenReference(parameters.getStrElement(), parameters.getData().getBSPEnforcer());
80          //
81          // Here we get some information about the document that is being
82          // processed, in particular the crypto implementation, and already
83          // detected BST that may be used later during dereferencing.
84          //
85          String uri = null;
86          if (secRef.getReference() != null) {
87              uri = secRef.getReference().getURI();
88              uri = XMLUtils.getIDFromReference(uri);
89          } else if (secRef.containsKeyIdentifier()) {
90              uri = secRef.getKeyIdentifierValue();
91          }
92  
93          WSSecurityEngineResult result = parameters.getData().getWsDocInfo().getResult(uri);
94          if (result != null) {
95              return processPreviousResult(result, secRef, parameters);
96          }
97  
98          return processSTR(secRef, uri, parameters);
99      }
100 
101     /**
102      * A method to create a Principal from a SAML Assertion
103      * @param samlAssertion An SamlAssertionWrapper object
104      * @param secretKey the raw secret key (if any) taken from the assertion's Subject KeyInfo,
105      *                  which is only a meaningful proof-of-possession credential for the
106      *                  holder-of-key confirmation method
107      * @return A principal
108      */
109     private Principal createPrincipalFromSAML(
110         SamlAssertionWrapper samlAssertion, STRParserResult parserResult, byte[] secretKey
111     ) throws WSSecurityException {
112         SAMLTokenPrincipalImpl samlPrincipal = new SAMLTokenPrincipalImpl(samlAssertion);
113         String confirmMethod = null;
114         List<String> methods = samlAssertion.getConfirmationMethods();
115         if (methods != null && !methods.isEmpty()) {
116             confirmMethod = methods.get(0);
117         }
118         boolean holderOfKey = OpenSAMLUtil.isMethodHolderOfKey(confirmMethod);
119         if (holderOfKey && samlAssertion.isSigned()) {
120             parserResult.setTrustedCredential(true);
121         } else if (!holderOfKey && secretKey != null && secretKey.length > 0) {
122             // A raw secret key from the Subject KeyInfo of a non-holder-of-key assertion (e.g.
123             // sender-vouches) has no defined proof-of-possession semantics, and no trust anchor is
124             // ever checked for it (SignatureTrustValidator only validates certs / public keys). Using
125             // such a key to "verify" a signature is a self-referential no-op that an attacker can
126             // trivially satisfy by embedding their own key in an unsigned assertion, so reject it.
127             throw new WSSecurityException(
128                 WSSecurityException.ErrorCode.FAILED_CHECK, "invalidSAMLsecurity"
129             );
130         }
131         return samlPrincipal;
132     }
133 
134     /**
135      * Parse the KeyIdentifier for a SAML Assertion
136      */
137     private void parseSAMLKeyIdentifier(
138         SecurityTokenReference secRef,
139         RequestData data,
140         STRParserResult parserResult
141     ) throws WSSecurityException {
142         String valueType = secRef.getKeyIdentifierValueType();
143         byte[] secretKey = STRParserUtil.getSecretKeyFromToken(secRef.getKeyIdentifierValue(), valueType,
144                                                                WSPasswordCallback.SECRET_KEY, data);
145         if (secretKey == null || secretKey.length == 0) {
146             SamlAssertionWrapper samlAssertion =
147                 STRParserUtil.getAssertionFromKeyIdentifier(
148                     secRef, secRef.getElement(), data
149                 );
150             STRParserUtil.checkSamlTokenBSPCompliance(secRef, samlAssertion, data.getBSPEnforcer());
151 
152             SAMLKeyInfo samlKi =
153                 SAMLUtil.getCredentialFromSubject(samlAssertion,
154                         new WSSSAMLKeyInfoProcessor(data), data.getSigVerCrypto());
155             X509Certificate[] foundCerts = samlKi.getCerts();
156             if (foundCerts != null && foundCerts.length > 0) {
157                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
158             }
159             secretKey = samlKi.getSecret();
160             parserResult.setPublicKey(samlKi.getPublicKey());
161             parserResult.setPrincipal(createPrincipalFromSAML(samlAssertion, parserResult, secretKey));
162         }
163         parserResult.setSecretKey(secretKey);
164     }
165 
166     /**
167      * Parse the KeyIdentifier for a BinarySecurityToken
168      */
169     private void parseBSTKeyIdentifier(
170         SecurityTokenReference secRef,
171         Crypto crypto,
172         RequestData data,
173         STRParserResult parserResult
174     ) throws WSSecurityException {
175         STRParserUtil.checkBinarySecurityBSPCompliance(secRef, null, data.getBSPEnforcer());
176 
177         String valueType = secRef.getKeyIdentifierValueType();
178         if (WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(valueType)) {
179             byte[] secretKey =
180                 STRParserUtil.getSecretKeyFromToken(secRef.getKeyIdentifierValue(), valueType,
181                                                     WSPasswordCallback.SECRET_KEY, data);
182             if (secretKey == null || secretKey.length == 0) {
183                 byte[] keyBytes = secRef.getSKIBytes();
184                 List<WSSecurityEngineResult> resultsList =
185                     data.getWsDocInfo().getResultsByTag(WSConstants.BST);
186                 for (WSSecurityEngineResult bstResult : resultsList) {
187                     BinarySecurity bstToken =
188                         (BinarySecurity)bstResult.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
189                     byte[] tokenDigest = KeyUtils.generateDigest(bstToken.getToken());
190                     if (Arrays.equals(tokenDigest, keyBytes)) {
191                         secretKey = (byte[])bstResult.get(WSSecurityEngineResult.TAG_SECRET);
192                         parserResult.setPrincipal((Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL));
193                         break;
194                     }
195                 }
196             } else {
197                 parserResult.setPrincipal(new CustomTokenPrincipal(secRef.getKeyIdentifierValue()));
198             }
199             parserResult.setSecretKey(secretKey);
200         } else {
201             X509Certificate[] foundCerts = secRef.getKeyIdentifier(crypto);
202             if (foundCerts == null || foundCerts.length == 0) {
203                 // The reference may be to a BST in the security header rather than in the keystore
204                 if (SecurityTokenReference.SKI_URI.equals(valueType)) {
205                     byte[] skiBytes = secRef.getSKIBytes();
206                     List<WSSecurityEngineResult> resultsList =
207                         data.getWsDocInfo().getResultsByTag(WSConstants.BST);
208                     for (WSSecurityEngineResult bstResult : resultsList) {
209                         X509Certificate[] certs =
210                             (X509Certificate[])bstResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
211                         if (certs != null && certs.length > 0
212                             && Arrays.equals(skiBytes, crypto.getSKIBytesFromCert(certs[0]))) {
213                             parserResult.setPrincipal((Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL));
214                             foundCerts = certs;
215                             break;
216                         }
217                     }
218                 } else if (SecurityTokenReference.THUMB_URI.equals(valueType)) {
219                     String kiValue = secRef.getKeyIdentifierValue();
220                     List<WSSecurityEngineResult> resultsList =
221                         data.getWsDocInfo().getResultsByTag(WSConstants.BST);
222                     for (WSSecurityEngineResult bstResult : resultsList) {
223                         X509Certificate[] certs =
224                             (X509Certificate[])bstResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
225                         if (certs != null && certs.length > 0) {
226                             try {
227                                 byte[] digest = KeyUtils.generateDigest(certs[0].getEncoded());
228                                 if (Arrays.equals(org.apache.xml.security.utils.XMLUtils.decode(kiValue), digest)) {
229                                     parserResult.setPrincipal((Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL));
230                                     foundCerts = certs;
231                                     break;
232                                 }
233                             } catch (CertificateEncodingException ex) {
234                                 throw new WSSecurityException(
235                                     WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, ex, "encodeError"
236                                 );
237                             }
238                         }
239                     }
240                 }
241             }
242             if (foundCerts != null && foundCerts.length > 0) {
243                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
244             }
245         }
246     }
247 
248     /**
249      * Process a previous security result
250      */
251     private STRParserResult processPreviousResult(
252         WSSecurityEngineResult result,
253         SecurityTokenReference secRef,
254         STRParserParameters parameters
255     ) throws WSSecurityException {
256 
257         STRParserResult parserResult = new STRParserResult();
258         RequestData data = parameters.getData();
259 
260         Integer action = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
261         if (action != null
262             && (WSConstants.UT_NOPASSWORD == action.intValue() || WSConstants.UT == action.intValue())) {
263             STRParserUtil.checkUsernameTokenBSPCompliance(secRef, data.getBSPEnforcer());
264 
265             UsernameToken usernameToken =
266                 (UsernameToken)result.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
267 
268             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
269 
270             parserResult.setPrincipal(usernameToken.createPrincipal());
271         } else if (action != null && WSConstants.BST == action.intValue()) {
272             BinarySecurity token =
273                 (BinarySecurity)result.get(
274                     WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN
275                 );
276             STRParserUtil.checkBinarySecurityBSPCompliance(secRef, token, data.getBSPEnforcer());
277 
278             parserResult.setCerts(
279                 (X509Certificate[])result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES));
280             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
281             Boolean validatedToken =
282                 (Boolean)result.get(WSSecurityEngineResult.TAG_VALIDATED_TOKEN);
283             if (validatedToken) {
284                 parserResult.setTrustedCredential(true);
285             }
286         } else if (action != null && WSConstants.ENCR == action.intValue()) {
287             STRParserUtil.checkEncryptedKeyBSPCompliance(secRef, data.getBSPEnforcer());
288 
289             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
290             String id = (String)result.get(WSSecurityEngineResult.TAG_ID);
291             parserResult.setPrincipal(new CustomTokenPrincipal(id));
292         } else if (action != null && WSConstants.SCT == action.intValue()) {
293             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
294             SecurityContextToken sct =
295                 (SecurityContextToken)result.get(
296                         WSSecurityEngineResult.TAG_SECURITY_CONTEXT_TOKEN
297                 );
298             parserResult.setPrincipal(new CustomTokenPrincipal(sct.getIdentifier()));
299         } else if (action != null && WSConstants.DKT == action.intValue()) {
300             DerivedKeyToken dkt =
301                 (DerivedKeyToken)result.get(WSSecurityEngineResult.TAG_DERIVED_KEY_TOKEN);
302             int keyLength = dkt.getLength();
303             if (keyLength <= 0 && parameters.getDerivationKeyLength() > 0) {
304                 keyLength = parameters.getDerivationKeyLength();
305             }
306             byte[] secret = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
307             Principal principal = dkt.createPrincipal();
308             ((WSDerivedKeyTokenPrincipal)principal).setSecret(secret);
309             parserResult.setPrincipal(principal);
310             parserResult.setSecretKey(dkt.deriveKey(keyLength, secret));
311         } else if (action != null
312             && (WSConstants.ST_UNSIGNED == action.intValue() || WSConstants.ST_SIGNED == action.intValue())) {
313             SamlAssertionWrapper samlAssertion =
314                 (SamlAssertionWrapper)result.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
315             STRParserUtil.checkSamlTokenBSPCompliance(secRef, samlAssertion, data.getBSPEnforcer());
316 
317             SAMLKeyInfo keyInfo = samlAssertion.getSubjectKeyInfo();
318             if (keyInfo == null) {
319                 throw new WSSecurityException(
320                     WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"
321                 );
322             }
323             X509Certificate[] foundCerts = keyInfo.getCerts();
324             if (foundCerts != null && foundCerts.length > 0) {
325                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
326             }
327             parserResult.setSecretKey(keyInfo.getSecret());
328             parserResult.setPublicKey(keyInfo.getPublicKey());
329             parserResult.setPrincipal(createPrincipalFromSAML(samlAssertion, parserResult, keyInfo.getSecret()));
330         }
331 
332         REFERENCE_TYPE referenceType = getReferenceType(secRef);
333         if (referenceType != null) {
334             parserResult.setReferenceType(referenceType);
335         }
336 
337         return parserResult;
338     }
339 
340     private STRParserResult processSTR(
341         SecurityTokenReference secRef,
342         String uri,
343         STRParserParameters parameters
344     ) throws WSSecurityException {
345         STRParserResult parserResult = new STRParserResult();
346         RequestData data = parameters.getData();
347         WSDocInfo wsDocInfo = data.getWsDocInfo();
348         Element strElement = parameters.getStrElement();
349 
350         if (secRef.containsReference()) {
351             Reference reference = secRef.getReference();
352             // Try asking the CallbackHandler for the secret key
353             byte[] secretKey = STRParserUtil.getSecretKeyFromToken(uri, reference.getValueType(),
354                                                                    WSPasswordCallback.SECRET_KEY,
355                                                                    data);
356             Principal principal = new CustomTokenPrincipal(uri);
357 
358             if (secretKey == null || secretKey.length == 0) {
359                 Element token =
360                     STRParserUtil.getTokenElement(strElement.getOwnerDocument(), wsDocInfo, data.getCallbackHandler(),
361                                                   uri, reference.getValueType());
362                 QName el = new QName(token.getNamespaceURI(), token.getLocalName());
363                 if (el.equals(WSConstants.BINARY_TOKEN)) {
364                     Processor proc = data.getWssConfig().getProcessor(WSConstants.BINARY_TOKEN);
365                     List<WSSecurityEngineResult> bstResult = proc.handleToken(token, parameters.getData());
366                     BinarySecurity bstToken =
367                         (BinarySecurity)bstResult.get(0).get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
368                     STRParserUtil.checkBinarySecurityBSPCompliance(
369                         secRef, bstToken, data.getBSPEnforcer()
370                     );
371 
372                     parserResult.setCerts(
373                         (X509Certificate[])bstResult.get(0).get(WSSecurityEngineResult.TAG_X509_CERTIFICATES));
374                     secretKey = (byte[])bstResult.get(0).get(WSSecurityEngineResult.TAG_SECRET);
375                     principal = (Principal)bstResult.get(0).get(WSSecurityEngineResult.TAG_PRINCIPAL);
376                 } else if (el.equals(WSConstants.SAML_TOKEN) || el.equals(WSConstants.SAML2_TOKEN)) {
377                     Processor proc = data.getWssConfig().getProcessor(WSConstants.SAML_TOKEN);
378                     //
379                     // Just check to see whether the token was processed or not
380                     //
381                     Element processedToken =
382                         STRParserUtil.findProcessedTokenElement(
383                             strElement.getOwnerDocument(), wsDocInfo,
384                             data.getCallbackHandler(), uri, secRef.getReference().getValueType()
385                         );
386                     SamlAssertionWrapper samlAssertion = null;
387                     if (processedToken == null) {
388                         List<WSSecurityEngineResult> samlResult = proc.handleToken(token, data);
389                         samlAssertion =
390                             (SamlAssertionWrapper)samlResult.get(0).get(
391                                 WSSecurityEngineResult.TAG_SAML_ASSERTION
392                             );
393                     } else {
394                         samlAssertion = new SamlAssertionWrapper(processedToken);
395                         samlAssertion.parseSubject(
396                             new WSSSAMLKeyInfoProcessor(data), data.getSigVerCrypto()
397                         );
398                     }
399                     STRParserUtil.checkSamlTokenBSPCompliance(secRef, samlAssertion, data.getBSPEnforcer());
400 
401                     SAMLKeyInfo keyInfo = samlAssertion.getSubjectKeyInfo();
402                     X509Certificate[] foundCerts = keyInfo.getCerts();
403                     if (foundCerts != null && foundCerts.length > 0) {
404                         parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
405                     }
406                     secretKey = keyInfo.getSecret();
407                     principal = createPrincipalFromSAML(samlAssertion, parserResult, secretKey);
408                 } else if (el.equals(WSConstants.ENCRYPTED_KEY)) {
409                     STRParserUtil.checkEncryptedKeyBSPCompliance(secRef, data.getBSPEnforcer());
410                     Processor proc = data.getWssConfig().getProcessor(WSConstants.ENCRYPTED_KEY);
411                     List<WSSecurityEngineResult> encrResult = proc.handleToken(token, data);
412                     secretKey =
413                         (byte[])encrResult.get(0).get(WSSecurityEngineResult.TAG_SECRET);
414                     principal = new CustomTokenPrincipal(token.getAttributeNS(null, "Id"));
415                 }
416             }
417 
418             parserResult.setSecretKey(secretKey);
419             parserResult.setPrincipal(principal);
420         } else if (secRef.containsX509Data() || secRef.containsX509IssuerSerial()) {
421             parserResult.setReferenceType(REFERENCE_TYPE.ISSUER_SERIAL);
422             Crypto crypto = data.getSigVerCrypto();
423             X509Certificate[] foundCerts = secRef.getX509IssuerSerial(crypto);
424             if (foundCerts != null && foundCerts.length > 0) {
425                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
426             }
427         } else if (secRef.containsKeyIdentifier()) {
428             if (secRef.getKeyIdentifierValueType().equals(SecurityTokenReference.ENC_KEY_SHA1_URI)) {
429                 STRParserUtil.checkEncryptedKeyBSPCompliance(secRef, data.getBSPEnforcer());
430 
431                 String id = secRef.getKeyIdentifierValue();
432                 parserResult.setSecretKey(
433                     STRParserUtil.getSecretKeyFromToken(id, SecurityTokenReference.ENC_KEY_SHA1_URI,
434                                                         WSPasswordCallback.SECRET_KEY, data));
435                 parserResult.setPrincipal(new CustomTokenPrincipal(id));
436             } else if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())
437                 || WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())) {
438                 parseSAMLKeyIdentifier(secRef, data, parserResult);
439             } else {
440                 Crypto crypto = data.getSigVerCrypto();
441                 parseBSTKeyIdentifier(secRef, crypto, data, parserResult);
442             }
443         } else {
444             throw new WSSecurityException(
445                     WSSecurityException.ErrorCode.INVALID_SECURITY,
446                     "unsupportedKeyInfo", new Object[] {strElement.toString()});
447         }
448 
449         REFERENCE_TYPE referenceType = getReferenceType(secRef);
450         if (referenceType != null) {
451             parserResult.setReferenceType(referenceType);
452         }
453 
454         return parserResult;
455     }
456 
457     private REFERENCE_TYPE getReferenceType(SecurityTokenReference secRef) {
458         if (secRef.containsReference()) {
459             return REFERENCE_TYPE.DIRECT_REF;
460         } else if (secRef.containsKeyIdentifier()) {
461             if (SecurityTokenReference.THUMB_URI.equals(secRef.getKeyIdentifierValueType())) {
462                 return REFERENCE_TYPE.THUMBPRINT_SHA1;
463             } else {
464                 return REFERENCE_TYPE.KEY_IDENTIFIER;
465             }
466         }
467 
468         return null;
469     }
470 
471 }