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      * @return A principal
105      */
106     private Principal createPrincipalFromSAML(
107         SamlAssertionWrapper samlAssertion, STRParserResult parserResult
108     ) {
109         SAMLTokenPrincipalImpl samlPrincipal = new SAMLTokenPrincipalImpl(samlAssertion);
110         String confirmMethod = null;
111         List<String> methods = samlAssertion.getConfirmationMethods();
112         if (methods != null && !methods.isEmpty()) {
113             confirmMethod = methods.get(0);
114         }
115         if (OpenSAMLUtil.isMethodHolderOfKey(confirmMethod) && samlAssertion.isSigned()) {
116             parserResult.setTrustedCredential(true);
117         }
118         return samlPrincipal;
119     }
120 
121     /**
122      * Parse the KeyIdentifier for a SAML Assertion
123      */
124     private void parseSAMLKeyIdentifier(
125         SecurityTokenReference secRef,
126         RequestData data,
127         STRParserResult parserResult
128     ) throws WSSecurityException {
129         String valueType = secRef.getKeyIdentifierValueType();
130         byte[] secretKey = STRParserUtil.getSecretKeyFromToken(secRef.getKeyIdentifierValue(), valueType,
131                                                                WSPasswordCallback.SECRET_KEY, data);
132         if (secretKey == null || secretKey.length == 0) {
133             SamlAssertionWrapper samlAssertion =
134                 STRParserUtil.getAssertionFromKeyIdentifier(
135                     secRef, secRef.getElement(), data
136                 );
137             STRParserUtil.checkSamlTokenBSPCompliance(secRef, samlAssertion, data.getBSPEnforcer());
138 
139             SAMLKeyInfo samlKi =
140                 SAMLUtil.getCredentialFromSubject(samlAssertion,
141                         new WSSSAMLKeyInfoProcessor(data), data.getSigVerCrypto());
142             X509Certificate[] foundCerts = samlKi.getCerts();
143             if (foundCerts != null && foundCerts.length > 0) {
144                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
145             }
146             secretKey = samlKi.getSecret();
147             parserResult.setPublicKey(samlKi.getPublicKey());
148             parserResult.setPrincipal(createPrincipalFromSAML(samlAssertion, parserResult));
149         }
150         parserResult.setSecretKey(secretKey);
151     }
152 
153     /**
154      * Parse the KeyIdentifier for a BinarySecurityToken
155      */
156     private void parseBSTKeyIdentifier(
157         SecurityTokenReference secRef,
158         Crypto crypto,
159         RequestData data,
160         STRParserResult parserResult
161     ) throws WSSecurityException {
162         STRParserUtil.checkBinarySecurityBSPCompliance(secRef, null, data.getBSPEnforcer());
163 
164         String valueType = secRef.getKeyIdentifierValueType();
165         if (WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(valueType)) {
166             byte[] secretKey =
167                 STRParserUtil.getSecretKeyFromToken(secRef.getKeyIdentifierValue(), valueType,
168                                                     WSPasswordCallback.SECRET_KEY, data);
169             if (secretKey == null || secretKey.length == 0) {
170                 byte[] keyBytes = secRef.getSKIBytes();
171                 List<WSSecurityEngineResult> resultsList =
172                     data.getWsDocInfo().getResultsByTag(WSConstants.BST);
173                 for (WSSecurityEngineResult bstResult : resultsList) {
174                     BinarySecurity bstToken =
175                         (BinarySecurity)bstResult.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
176                     byte[] tokenDigest = KeyUtils.generateDigest(bstToken.getToken());
177                     if (Arrays.equals(tokenDigest, keyBytes)) {
178                         secretKey = (byte[])bstResult.get(WSSecurityEngineResult.TAG_SECRET);
179                         parserResult.setPrincipal((Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL));
180                         break;
181                     }
182                 }
183             } else {
184                 parserResult.setPrincipal(new CustomTokenPrincipal(secRef.getKeyIdentifierValue()));
185             }
186             parserResult.setSecretKey(secretKey);
187         } else {
188             X509Certificate[] foundCerts = secRef.getKeyIdentifier(crypto);
189             if (foundCerts == null || foundCerts.length == 0) {
190                 // The reference may be to a BST in the security header rather than in the keystore
191                 if (SecurityTokenReference.SKI_URI.equals(valueType)) {
192                     byte[] skiBytes = secRef.getSKIBytes();
193                     List<WSSecurityEngineResult> resultsList =
194                         data.getWsDocInfo().getResultsByTag(WSConstants.BST);
195                     for (WSSecurityEngineResult bstResult : resultsList) {
196                         X509Certificate[] certs =
197                             (X509Certificate[])bstResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
198                         if (certs != null && certs.length > 0
199                             && Arrays.equals(skiBytes, crypto.getSKIBytesFromCert(certs[0]))) {
200                             parserResult.setPrincipal((Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL));
201                             foundCerts = certs;
202                             break;
203                         }
204                     }
205                 } else if (SecurityTokenReference.THUMB_URI.equals(valueType)) {
206                     String kiValue = secRef.getKeyIdentifierValue();
207                     List<WSSecurityEngineResult> resultsList =
208                         data.getWsDocInfo().getResultsByTag(WSConstants.BST);
209                     for (WSSecurityEngineResult bstResult : resultsList) {
210                         X509Certificate[] certs =
211                             (X509Certificate[])bstResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
212                         if (certs != null && certs.length > 0) {
213                             try {
214                                 byte[] digest = KeyUtils.generateDigest(certs[0].getEncoded());
215                                 if (Arrays.equals(org.apache.xml.security.utils.XMLUtils.decode(kiValue), digest)) {
216                                     parserResult.setPrincipal((Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL));
217                                     foundCerts = certs;
218                                     break;
219                                 }
220                             } catch (CertificateEncodingException ex) {
221                                 throw new WSSecurityException(
222                                     WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, ex, "encodeError"
223                                 );
224                             }
225                         }
226                     }
227                 }
228             }
229             if (foundCerts != null && foundCerts.length > 0) {
230                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
231             }
232         }
233     }
234 
235     /**
236      * Process a previous security result
237      */
238     private STRParserResult processPreviousResult(
239         WSSecurityEngineResult result,
240         SecurityTokenReference secRef,
241         STRParserParameters parameters
242     ) throws WSSecurityException {
243 
244         STRParserResult parserResult = new STRParserResult();
245         RequestData data = parameters.getData();
246 
247         Integer action = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
248         if (action != null
249             && (WSConstants.UT_NOPASSWORD == action.intValue() || WSConstants.UT == action.intValue())) {
250             STRParserUtil.checkUsernameTokenBSPCompliance(secRef, data.getBSPEnforcer());
251 
252             UsernameToken usernameToken =
253                 (UsernameToken)result.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
254 
255             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
256 
257             parserResult.setPrincipal(usernameToken.createPrincipal());
258         } else if (action != null && WSConstants.BST == action.intValue()) {
259             BinarySecurity token =
260                 (BinarySecurity)result.get(
261                     WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN
262                 );
263             STRParserUtil.checkBinarySecurityBSPCompliance(secRef, token, data.getBSPEnforcer());
264 
265             parserResult.setCerts(
266                 (X509Certificate[])result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES));
267             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
268             Boolean validatedToken =
269                 (Boolean)result.get(WSSecurityEngineResult.TAG_VALIDATED_TOKEN);
270             if (validatedToken) {
271                 parserResult.setTrustedCredential(true);
272             }
273         } else if (action != null && WSConstants.ENCR == action.intValue()) {
274             STRParserUtil.checkEncryptedKeyBSPCompliance(secRef, data.getBSPEnforcer());
275 
276             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
277             String id = (String)result.get(WSSecurityEngineResult.TAG_ID);
278             parserResult.setPrincipal(new CustomTokenPrincipal(id));
279         } else if (action != null && WSConstants.SCT == action.intValue()) {
280             parserResult.setSecretKey((byte[])result.get(WSSecurityEngineResult.TAG_SECRET));
281             SecurityContextToken sct =
282                 (SecurityContextToken)result.get(
283                         WSSecurityEngineResult.TAG_SECURITY_CONTEXT_TOKEN
284                 );
285             parserResult.setPrincipal(new CustomTokenPrincipal(sct.getIdentifier()));
286         } else if (action != null && WSConstants.DKT == action.intValue()) {
287             DerivedKeyToken dkt =
288                 (DerivedKeyToken)result.get(WSSecurityEngineResult.TAG_DERIVED_KEY_TOKEN);
289             int keyLength = dkt.getLength();
290             if (keyLength <= 0 && parameters.getDerivationKeyLength() > 0) {
291                 keyLength = parameters.getDerivationKeyLength();
292             }
293             byte[] secret = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
294             Principal principal = dkt.createPrincipal();
295             ((WSDerivedKeyTokenPrincipal)principal).setSecret(secret);
296             parserResult.setPrincipal(principal);
297             parserResult.setSecretKey(dkt.deriveKey(keyLength, secret));
298         } else if (action != null
299             && (WSConstants.ST_UNSIGNED == action.intValue() || WSConstants.ST_SIGNED == action.intValue())) {
300             SamlAssertionWrapper samlAssertion =
301                 (SamlAssertionWrapper)result.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
302             STRParserUtil.checkSamlTokenBSPCompliance(secRef, samlAssertion, data.getBSPEnforcer());
303 
304             SAMLKeyInfo keyInfo = samlAssertion.getSubjectKeyInfo();
305             if (keyInfo == null) {
306                 throw new WSSecurityException(
307                     WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"
308                 );
309             }
310             X509Certificate[] foundCerts = keyInfo.getCerts();
311             if (foundCerts != null && foundCerts.length > 0) {
312                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
313             }
314             parserResult.setSecretKey(keyInfo.getSecret());
315             parserResult.setPublicKey(keyInfo.getPublicKey());
316             parserResult.setPrincipal(createPrincipalFromSAML(samlAssertion, parserResult));
317         }
318 
319         REFERENCE_TYPE referenceType = getReferenceType(secRef);
320         if (referenceType != null) {
321             parserResult.setReferenceType(referenceType);
322         }
323 
324         return parserResult;
325     }
326 
327     private STRParserResult processSTR(
328         SecurityTokenReference secRef,
329         String uri,
330         STRParserParameters parameters
331     ) throws WSSecurityException {
332         STRParserResult parserResult = new STRParserResult();
333         RequestData data = parameters.getData();
334         WSDocInfo wsDocInfo = data.getWsDocInfo();
335         Element strElement = parameters.getStrElement();
336 
337         if (secRef.containsReference()) {
338             Reference reference = secRef.getReference();
339             // Try asking the CallbackHandler for the secret key
340             byte[] secretKey = STRParserUtil.getSecretKeyFromToken(uri, reference.getValueType(),
341                                                                    WSPasswordCallback.SECRET_KEY,
342                                                                    data);
343             Principal principal = new CustomTokenPrincipal(uri);
344 
345             if (secretKey == null || secretKey.length == 0) {
346                 Element token =
347                     STRParserUtil.getTokenElement(strElement.getOwnerDocument(), wsDocInfo, data.getCallbackHandler(),
348                                                   uri, reference.getValueType());
349                 QName el = new QName(token.getNamespaceURI(), token.getLocalName());
350                 if (el.equals(WSConstants.BINARY_TOKEN)) {
351                     Processor proc = data.getWssConfig().getProcessor(WSConstants.BINARY_TOKEN);
352                     List<WSSecurityEngineResult> bstResult = proc.handleToken(token, parameters.getData());
353                     BinarySecurity bstToken =
354                         (BinarySecurity)bstResult.get(0).get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
355                     STRParserUtil.checkBinarySecurityBSPCompliance(
356                         secRef, bstToken, data.getBSPEnforcer()
357                     );
358 
359                     parserResult.setCerts(
360                         (X509Certificate[])bstResult.get(0).get(WSSecurityEngineResult.TAG_X509_CERTIFICATES));
361                     secretKey = (byte[])bstResult.get(0).get(WSSecurityEngineResult.TAG_SECRET);
362                     principal = (Principal)bstResult.get(0).get(WSSecurityEngineResult.TAG_PRINCIPAL);
363                 } else if (el.equals(WSConstants.SAML_TOKEN) || el.equals(WSConstants.SAML2_TOKEN)) {
364                     Processor proc = data.getWssConfig().getProcessor(WSConstants.SAML_TOKEN);
365                     //
366                     // Just check to see whether the token was processed or not
367                     //
368                     Element processedToken =
369                         STRParserUtil.findProcessedTokenElement(
370                             strElement.getOwnerDocument(), wsDocInfo,
371                             data.getCallbackHandler(), uri, secRef.getReference().getValueType()
372                         );
373                     SamlAssertionWrapper samlAssertion = null;
374                     if (processedToken == null) {
375                         List<WSSecurityEngineResult> samlResult = proc.handleToken(token, data);
376                         samlAssertion =
377                             (SamlAssertionWrapper)samlResult.get(0).get(
378                                 WSSecurityEngineResult.TAG_SAML_ASSERTION
379                             );
380                     } else {
381                         samlAssertion = new SamlAssertionWrapper(processedToken);
382                         samlAssertion.parseSubject(
383                             new WSSSAMLKeyInfoProcessor(data), data.getSigVerCrypto()
384                         );
385                     }
386                     STRParserUtil.checkSamlTokenBSPCompliance(secRef, samlAssertion, data.getBSPEnforcer());
387 
388                     SAMLKeyInfo keyInfo = samlAssertion.getSubjectKeyInfo();
389                     X509Certificate[] foundCerts = keyInfo.getCerts();
390                     if (foundCerts != null && foundCerts.length > 0) {
391                         parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
392                     }
393                     secretKey = keyInfo.getSecret();
394                     principal = createPrincipalFromSAML(samlAssertion, parserResult);
395                 } else if (el.equals(WSConstants.ENCRYPTED_KEY)) {
396                     STRParserUtil.checkEncryptedKeyBSPCompliance(secRef, data.getBSPEnforcer());
397                     Processor proc = data.getWssConfig().getProcessor(WSConstants.ENCRYPTED_KEY);
398                     List<WSSecurityEngineResult> encrResult = proc.handleToken(token, data);
399                     secretKey =
400                         (byte[])encrResult.get(0).get(WSSecurityEngineResult.TAG_SECRET);
401                     principal = new CustomTokenPrincipal(token.getAttributeNS(null, "Id"));
402                 }
403             }
404 
405             parserResult.setSecretKey(secretKey);
406             parserResult.setPrincipal(principal);
407         } else if (secRef.containsX509Data() || secRef.containsX509IssuerSerial()) {
408             parserResult.setReferenceType(REFERENCE_TYPE.ISSUER_SERIAL);
409             Crypto crypto = data.getSigVerCrypto();
410             X509Certificate[] foundCerts = secRef.getX509IssuerSerial(crypto);
411             if (foundCerts != null && foundCerts.length > 0) {
412                 parserResult.setCerts(new X509Certificate[]{foundCerts[0]});
413             }
414         } else if (secRef.containsKeyIdentifier()) {
415             if (secRef.getKeyIdentifierValueType().equals(SecurityTokenReference.ENC_KEY_SHA1_URI)) {
416                 STRParserUtil.checkEncryptedKeyBSPCompliance(secRef, data.getBSPEnforcer());
417 
418                 String id = secRef.getKeyIdentifierValue();
419                 parserResult.setSecretKey(
420                     STRParserUtil.getSecretKeyFromToken(id, SecurityTokenReference.ENC_KEY_SHA1_URI,
421                                                         WSPasswordCallback.SECRET_KEY, data));
422                 parserResult.setPrincipal(new CustomTokenPrincipal(id));
423             } else if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())
424                 || WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())) {
425                 parseSAMLKeyIdentifier(secRef, data, parserResult);
426             } else {
427                 Crypto crypto = data.getSigVerCrypto();
428                 parseBSTKeyIdentifier(secRef, crypto, data, parserResult);
429             }
430         } else {
431             throw new WSSecurityException(
432                     WSSecurityException.ErrorCode.INVALID_SECURITY,
433                     "unsupportedKeyInfo", new Object[] {strElement.toString()});
434         }
435 
436         REFERENCE_TYPE referenceType = getReferenceType(secRef);
437         if (referenceType != null) {
438             parserResult.setReferenceType(referenceType);
439         }
440 
441         return parserResult;
442     }
443 
444     private REFERENCE_TYPE getReferenceType(SecurityTokenReference secRef) {
445         if (secRef.containsReference()) {
446             return REFERENCE_TYPE.DIRECT_REF;
447         } else if (secRef.containsKeyIdentifier()) {
448             if (SecurityTokenReference.THUMB_URI.equals(secRef.getKeyIdentifierValueType())) {
449                 return REFERENCE_TYPE.THUMBPRINT_SHA1;
450             } else {
451                 return REFERENCE_TYPE.KEY_IDENTIFIER;
452             }
453         }
454 
455         return null;
456     }
457 
458 }