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.ws.security.str;
21  
22  import org.apache.ws.security.CustomTokenPrincipal;
23  import org.apache.ws.security.SAMLTokenPrincipal;
24  import org.apache.ws.security.WSConstants;
25  import org.apache.ws.security.WSDerivedKeyTokenPrincipal;
26  import org.apache.ws.security.WSDocInfo;
27  import org.apache.ws.security.WSPasswordCallback;
28  import org.apache.ws.security.WSSecurityEngine;
29  import org.apache.ws.security.WSSecurityEngineResult;
30  import org.apache.ws.security.WSSecurityException;
31  import org.apache.ws.security.components.crypto.Crypto;
32  import org.apache.ws.security.handler.RequestData;
33  import org.apache.ws.security.message.token.BinarySecurity;
34  import org.apache.ws.security.message.token.DerivedKeyToken;
35  import org.apache.ws.security.message.token.Reference;
36  import org.apache.ws.security.message.token.SecurityContextToken;
37  import org.apache.ws.security.message.token.SecurityTokenReference;
38  import org.apache.ws.security.message.token.UsernameToken;
39  import org.apache.ws.security.processor.Processor;
40  import org.apache.ws.security.saml.SAMLKeyInfo;
41  import org.apache.ws.security.saml.SAMLUtil;
42  import org.apache.ws.security.saml.ext.AssertionWrapper;
43  import org.apache.ws.security.saml.ext.OpenSAMLUtil;
44  import org.apache.ws.security.util.Base64;
45  import org.apache.ws.security.util.WSSecurityUtil;
46  import org.w3c.dom.Element;
47  
48  import java.security.Principal;
49  import java.security.PublicKey;
50  import java.security.cert.CertificateEncodingException;
51  import java.security.cert.X509Certificate;
52  import java.util.Arrays;
53  import java.util.List;
54  import java.util.Map;
55  
56  import javax.security.auth.callback.Callback;
57  import javax.xml.namespace.QName;
58  
59  /**
60   * This implementation of STRParser is for parsing a SecurityTokenReference element, found in the
61   * KeyInfo element associated with a Signature element.
62   */
63  public class SignatureSTRParser implements STRParser {
64      
65      /**
66       * The Signature method. This is used when deriving a key to use for verifying the signature.
67       */
68      public static final String SIGNATURE_METHOD = "signature_method";
69      
70      /**
71       * The secret key length. This is used when deriving a key from a Username token for the
72       * non-standard WSE implementation.
73       */
74      public static final String SECRET_KEY_LENGTH = "secret_key_length";
75      
76      private X509Certificate[] certs;
77      
78      private byte[] secretKey;
79      
80      private PublicKey publicKey;
81      
82      private Principal principal;
83      
84      private boolean trustedCredential;
85      
86      private REFERENCE_TYPE referenceType;
87      
88      /**
89       * Parse a SecurityTokenReference element and extract credentials.
90       * 
91       * @param strElement The SecurityTokenReference element
92       * @param data the RequestData associated with the request
93       * @param wsDocInfo The WSDocInfo object to access previous processing results
94       * @param parameters A set of implementation-specific parameters
95       * @throws WSSecurityException
96       */
97      public void parseSecurityTokenReference(
98          Element strElement,
99          RequestData data,
100         WSDocInfo wsDocInfo,
101         Map<String, Object> parameters
102     ) throws WSSecurityException {
103         boolean bspCompliant = true;
104         Crypto crypto = data.getSigCrypto();
105         if (data.getWssConfig() != null) {
106             bspCompliant = data.getWssConfig().isWsiBSPCompliant();
107         }
108         SecurityTokenReference secRef = new SecurityTokenReference(strElement, bspCompliant);
109         //
110         // Here we get some information about the document that is being
111         // processed, in particular the crypto implementation, and already
112         // detected BST that may be used later during dereferencing.
113         //
114         String uri = null;
115         if (secRef.containsReference()) {
116             uri = secRef.getReference().getURI();
117             if (uri.charAt(0) == '#') {
118                 uri = uri.substring(1);
119             }
120             referenceType = REFERENCE_TYPE.DIRECT_REF;
121         } else if (secRef.containsKeyIdentifier()) {
122             uri = secRef.getKeyIdentifierValue();
123             if (SecurityTokenReference.THUMB_URI.equals(secRef.getKeyIdentifierValueType())) {
124                 referenceType = REFERENCE_TYPE.THUMBPRINT_SHA1;
125             } else {
126                 referenceType = REFERENCE_TYPE.KEY_IDENTIFIER;
127             }
128         }
129         
130         WSSecurityEngineResult result = wsDocInfo.getResult(uri);
131         if (result != null) {
132             processPreviousResult(result, secRef, data, parameters, bspCompliant);
133         } else if (secRef.containsReference()) {
134             Reference reference = secRef.getReference();
135             // Try asking the CallbackHandler for the secret key
136             secretKey = getSecretKeyFromToken(uri, reference.getValueType(), data);
137             principal = new CustomTokenPrincipal(uri);
138             
139             if (secretKey == null) {
140                 Element token = 
141                     secRef.getTokenElement(strElement.getOwnerDocument(), wsDocInfo, data.getCallbackHandler());
142                 QName el = new QName(token.getNamespaceURI(), token.getLocalName());
143                 if (el.equals(WSSecurityEngine.BINARY_TOKEN)) {
144                     Processor proc = data.getWssConfig().getProcessor(WSSecurityEngine.BINARY_TOKEN);
145                     List<WSSecurityEngineResult> bstResult =
146                         proc.handleToken(token, data, wsDocInfo);
147                     BinarySecurity bstToken = 
148                         (BinarySecurity)bstResult.get(0).get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
149                     if (bspCompliant) {
150                         BSPEnforcer.checkBinarySecurityBSPCompliance(secRef, bstToken);
151                     }
152                     certs = (X509Certificate[])bstResult.get(0).get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
153                     secretKey = (byte[])bstResult.get(0).get(WSSecurityEngineResult.TAG_SECRET);
154                     principal = (Principal)bstResult.get(0).get(WSSecurityEngineResult.TAG_PRINCIPAL);
155                 } else if (el.equals(WSSecurityEngine.SAML_TOKEN) 
156                     || el.equals(WSSecurityEngine.SAML2_TOKEN)) {
157                     Processor proc = data.getWssConfig().getProcessor(WSSecurityEngine.SAML_TOKEN);
158                     //
159                     // Just check to see whether the token was processed or not
160                     //
161                     Element processedToken = 
162                         secRef.findProcessedTokenElement(
163                             strElement.getOwnerDocument(), wsDocInfo, 
164                             data.getCallbackHandler(), uri, secRef.getReference().getValueType()
165                         );
166                     AssertionWrapper assertion = null;
167                     if (processedToken == null) {
168                         List<WSSecurityEngineResult> samlResult =
169                             proc.handleToken(token, data, wsDocInfo);
170                         assertion = 
171                             (AssertionWrapper)samlResult.get(0).get(
172                                 WSSecurityEngineResult.TAG_SAML_ASSERTION
173                             );
174                     } else {
175                         assertion = new AssertionWrapper(processedToken);
176                         assertion.parseHOKSubject(data, wsDocInfo);
177                     }
178                     if (bspCompliant) {
179                         BSPEnforcer.checkSamlTokenBSPCompliance(secRef, assertion);
180                     }
181                     SAMLKeyInfo keyInfo = assertion.getSubjectKeyInfo();
182                     X509Certificate[] foundCerts = keyInfo.getCerts();
183                     if (foundCerts != null && foundCerts.length > 0) {
184                         certs = new X509Certificate[]{foundCerts[0]};
185                     }
186                     secretKey = keyInfo.getSecret();
187                     principal = createPrincipalFromSAML(assertion);
188                 } else if (el.equals(WSSecurityEngine.ENCRYPTED_KEY)) {
189                     if (bspCompliant) {
190                         BSPEnforcer.checkEncryptedKeyBSPCompliance(secRef);
191                     }
192                     Processor proc = data.getWssConfig().getProcessor(WSSecurityEngine.ENCRYPTED_KEY);
193                     List<WSSecurityEngineResult> encrResult =
194                         proc.handleToken(token, data, wsDocInfo);
195                     secretKey = 
196                         (byte[])encrResult.get(0).get(WSSecurityEngineResult.TAG_SECRET);
197                     principal = new CustomTokenPrincipal(token.getAttribute("Id"));
198                 }
199             }
200         } else if (secRef.containsX509Data() || secRef.containsX509IssuerSerial()) {
201             referenceType = REFERENCE_TYPE.ISSUER_SERIAL;
202             X509Certificate[] foundCerts = secRef.getX509IssuerSerial(crypto);
203             if (foundCerts != null && foundCerts.length > 0) {
204                 certs = new X509Certificate[]{foundCerts[0]};
205             }
206         } else if (secRef.containsKeyIdentifier()) {
207             if (secRef.getKeyIdentifierValueType().equals(SecurityTokenReference.ENC_KEY_SHA1_URI)) {
208                 if (bspCompliant) {
209                     BSPEnforcer.checkEncryptedKeyBSPCompliance(secRef);
210                 }
211                 String id = secRef.getKeyIdentifierValue();
212                 secretKey = 
213                     getSecretKeyFromToken(id, SecurityTokenReference.ENC_KEY_SHA1_URI, data);
214                 principal = new CustomTokenPrincipal(id);
215             } else if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())
216                 || WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())) {
217                 parseSAMLKeyIdentifier(secRef, wsDocInfo, data, bspCompliant);
218             } else {
219                 parseBSTKeyIdentifier(secRef, crypto, wsDocInfo, data, bspCompliant);
220             }
221         } else {
222             throw new WSSecurityException(
223                     WSSecurityException.INVALID_SECURITY,
224                     "unsupportedKeyInfo", 
225                     new Object[]{strElement.toString()}
226             );
227         }
228         
229         if (certs != null && certs.length > 0 && principal == null) {
230             principal = certs[0].getSubjectX500Principal();
231         }
232     }
233     
234     /**
235      * Get the X509Certificates associated with this SecurityTokenReference
236      * @return the X509Certificates associated with this SecurityTokenReference
237      */
238     public X509Certificate[] getCertificates() {
239         return certs;
240     }
241     
242     /**
243      * Get the Principal associated with this SecurityTokenReference
244      * @return the Principal associated with this SecurityTokenReference
245      */
246     public Principal getPrincipal() {
247         return principal;
248     }
249     
250     /**
251      * Get the PublicKey associated with this SecurityTokenReference
252      * @return the PublicKey associated with this SecurityTokenReference
253      */
254     public PublicKey getPublicKey() {
255         return publicKey;
256     }
257     
258     /**
259      * Get the Secret Key associated with this SecurityTokenReference
260      * @return the Secret Key associated with this SecurityTokenReference
261      */
262     public byte[] getSecretKey() {
263         return secretKey;
264     }
265     
266     /**
267      * Get whether the returned credential is already trusted or not. This is currently
268      * applicable in the case of a credential extracted from a trusted HOK SAML Assertion,
269      * and a BinarySecurityToken that has been processed by a Validator. In these cases,
270      * the SignatureProcessor does not need to verify trust on the credential.
271      * @return true if trust has already been verified on the returned Credential
272      */
273     public boolean isTrustedCredential() {
274         return trustedCredential;
275     }
276     
277     /**
278      * Get how the certificates were referenced
279      * @return how the certificates were referenced
280      */
281     public REFERENCE_TYPE getCertificatesReferenceType() {
282         return referenceType;
283     }
284     
285     /**
286      * A method to create a Principal from a SAML Assertion
287      * @param assertion An AssertionWrapper object
288      * @return A principal
289      */
290     private Principal createPrincipalFromSAML(
291         AssertionWrapper assertion
292     ) {
293         SAMLTokenPrincipal samlPrincipal = new SAMLTokenPrincipal(assertion);
294         String confirmMethod = null;
295         List<String> methods = assertion.getConfirmationMethods();
296         if (methods != null && methods.size() > 0) {
297             confirmMethod = methods.get(0);
298         }
299         if (OpenSAMLUtil.isMethodHolderOfKey(confirmMethod) && assertion.isSigned()) {
300             trustedCredential = true;
301         }
302         return samlPrincipal;
303     }
304     
305     /**
306      * Get the Secret Key from a CallbackHandler
307      * @param id The id of the element
308      * @param type The type of the element (can be null)
309      * @param cb The CallbackHandler object
310      * @return A Secret Key
311      * @throws WSSecurityException
312      */
313     private byte[] getSecretKeyFromToken(
314         String id,
315         String type,
316         RequestData data
317     ) throws WSSecurityException {
318         if (id.charAt(0) == '#') {
319             id = id.substring(1);
320         }
321         WSPasswordCallback pwcb = 
322             new WSPasswordCallback(id, null, type, WSPasswordCallback.SECRET_KEY, data);
323         try {
324             Callback[] callbacks = new Callback[]{pwcb};
325             if (data.getCallbackHandler() != null) {
326                 data.getCallbackHandler().handle(callbacks);
327                 return pwcb.getKey();
328             }
329         } catch (Exception e) {
330             throw new WSSecurityException(
331                 WSSecurityException.FAILURE,
332                 "noPassword", 
333                 new Object[] {id}, 
334                 e
335             );
336         }
337 
338         return null;
339     }
340     
341     /**
342      * Parse the KeyIdentifier for a SAML Assertion
343      */
344     private void parseSAMLKeyIdentifier(
345         SecurityTokenReference secRef,
346         WSDocInfo wsDocInfo,
347         RequestData data,
348         boolean bspCompliant
349     ) throws WSSecurityException {
350         String valueType = secRef.getKeyIdentifierValueType();
351         secretKey = getSecretKeyFromToken(secRef.getKeyIdentifierValue(), valueType, data);
352         if (secretKey == null) {
353             AssertionWrapper assertion = 
354                 SAMLUtil.getAssertionFromKeyIdentifier(
355                     secRef, secRef.getElement(), data, wsDocInfo
356                 );
357             if (bspCompliant) {
358                 BSPEnforcer.checkSamlTokenBSPCompliance(secRef, assertion);
359             }
360             SAMLKeyInfo samlKi = 
361                 SAMLUtil.getCredentialFromSubject(assertion, data, wsDocInfo, bspCompliant);
362             X509Certificate[] foundCerts = samlKi.getCerts();
363             if (foundCerts != null && foundCerts.length > 0) {
364                 certs = new X509Certificate[]{foundCerts[0]};
365             }
366             secretKey = samlKi.getSecret();
367             publicKey = samlKi.getPublicKey();
368             principal = createPrincipalFromSAML(assertion);
369         }
370     }
371     
372     /**
373      * Parse the KeyIdentifier for a BinarySecurityToken
374      */
375     private void parseBSTKeyIdentifier(
376         SecurityTokenReference secRef,
377         Crypto crypto,
378         WSDocInfo wsDocInfo,
379         RequestData data,
380         boolean bspCompliant
381     ) throws WSSecurityException {
382         if (bspCompliant) {
383             BSPEnforcer.checkBinarySecurityBSPCompliance(secRef, null);
384         }
385         String valueType = secRef.getKeyIdentifierValueType();
386         if (WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(valueType)) {
387             secretKey = 
388                 getSecretKeyFromToken(secRef.getKeyIdentifierValue(), valueType, data);
389             if (secretKey == null) {
390                 byte[] keyBytes = secRef.getSKIBytes();
391                 List<WSSecurityEngineResult> resultsList = 
392                     wsDocInfo.getResultsByTag(WSConstants.BST);
393                 for (WSSecurityEngineResult bstResult : resultsList) {
394                     BinarySecurity bstToken = 
395                         (BinarySecurity)bstResult.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
396                     byte[] tokenDigest = WSSecurityUtil.generateDigest(bstToken.getToken());
397                     if (Arrays.equals(tokenDigest, keyBytes)) {
398                         secretKey = (byte[])bstResult.get(WSSecurityEngineResult.TAG_SECRET);
399                         principal = (Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
400                         break;
401                     }
402                 }
403             } else {
404                 principal = new CustomTokenPrincipal(secRef.getKeyIdentifierValue());
405             }
406         } else {
407             X509Certificate[] foundCerts = secRef.getKeyIdentifier(crypto);
408             if (foundCerts == null) {
409                 // The reference may be to a BST in the security header rather than in the keystore
410                 if (SecurityTokenReference.SKI_URI.equals(valueType)) {
411                     byte[] skiBytes = secRef.getSKIBytes();
412                     List<WSSecurityEngineResult> resultsList = 
413                         wsDocInfo.getResultsByTag(WSConstants.BST);
414                     for (WSSecurityEngineResult bstResult : resultsList) {
415                         X509Certificate[] certs = 
416                             (X509Certificate[])bstResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
417                         if (certs != null
418                             && Arrays.equals(skiBytes, crypto.getSKIBytesFromCert(certs[0]))) {
419                             principal = (Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
420                             foundCerts = certs;
421                             break;
422                         }
423                     }
424                 } else if (SecurityTokenReference.THUMB_URI.equals(valueType)) {
425                     String kiValue = secRef.getKeyIdentifierValue();
426                     List<WSSecurityEngineResult> resultsList = 
427                         wsDocInfo.getResultsByTag(WSConstants.BST);
428                     for (WSSecurityEngineResult bstResult : resultsList) {
429                         X509Certificate[] certs = 
430                             (X509Certificate[])bstResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
431                         if (certs != null) {
432                             try {
433                                 byte[] digest = WSSecurityUtil.generateDigest(certs[0].getEncoded());
434                                 if (Arrays.equals(Base64.decode(kiValue), digest)) {
435                                     principal = (Principal)bstResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
436                                     foundCerts = certs;
437                                     break;
438                                 }
439                             } catch (CertificateEncodingException ex) {
440                                 throw new WSSecurityException(
441                                     WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError",
442                                     null, ex
443                                 );
444                             }
445                         }
446                     }
447                 }
448             }
449             if (foundCerts != null) {
450                 certs = new X509Certificate[]{foundCerts[0]};
451             }
452         }
453     }
454     
455     /**
456      * Process a previous security result
457      */
458     private void processPreviousResult(
459         WSSecurityEngineResult result,
460         SecurityTokenReference secRef,
461         RequestData data,
462         Map<String, Object> parameters,
463         boolean bspCompliant
464     ) throws WSSecurityException {
465         int action = ((Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
466         if (WSConstants.UT_NOPASSWORD == action || WSConstants.UT == action) {
467             if (bspCompliant) {
468                 BSPEnforcer.checkUsernameTokenBSPCompliance(secRef);
469             }
470             UsernameToken usernameToken = 
471                 (UsernameToken)result.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
472 
473             usernameToken.setRawPassword(data);
474             if (usernameToken.isDerivedKey()) {
475                 secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
476             } else {
477                 int keyLength = ((Integer)parameters.get(SECRET_KEY_LENGTH)).intValue();
478                 secretKey = usernameToken.getSecretKey(keyLength);
479             }
480             principal = usernameToken.createPrincipal();
481         } else if (WSConstants.BST == action) {
482             if (bspCompliant) {
483                 BinarySecurity token = 
484                     (BinarySecurity)result.get(
485                         WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN
486                     );
487                 BSPEnforcer.checkBinarySecurityBSPCompliance(secRef, token);
488             }
489             certs = 
490                 (X509Certificate[])result.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
491             secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
492             Boolean validatedToken = 
493                 (Boolean)result.get(WSSecurityEngineResult.TAG_VALIDATED_TOKEN);
494             if (validatedToken.booleanValue()) {
495                 trustedCredential = true;
496             }
497         } else if (WSConstants.ENCR == action) {
498             if (bspCompliant) {
499                 BSPEnforcer.checkEncryptedKeyBSPCompliance(secRef);
500             }
501             secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
502             String id = (String)result.get(WSSecurityEngineResult.TAG_ID);
503             principal = new CustomTokenPrincipal(id);
504         } else if (WSConstants.SCT == action) {
505             secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
506             SecurityContextToken sct = 
507                 (SecurityContextToken)result.get(
508                         WSSecurityEngineResult.TAG_SECURITY_CONTEXT_TOKEN
509                 );
510             principal = new CustomTokenPrincipal(sct.getIdentifier());
511         } else if (WSConstants.DKT == action) {
512             DerivedKeyToken dkt = 
513                 (DerivedKeyToken)result.get(WSSecurityEngineResult.TAG_DERIVED_KEY_TOKEN);
514             int keyLength = dkt.getLength();
515             if (keyLength <= 0) {
516                 String algorithm = (String)parameters.get(SIGNATURE_METHOD);
517                 keyLength = WSSecurityUtil.getKeyLength(algorithm);
518             }
519             byte[] secret = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
520             secretKey = dkt.deriveKey(keyLength, secret); 
521             principal = dkt.createPrincipal();
522             ((WSDerivedKeyTokenPrincipal)principal).setSecret(secret);
523         } else if (WSConstants.ST_UNSIGNED == action || WSConstants.ST_SIGNED == action) {
524             AssertionWrapper assertion = 
525                 (AssertionWrapper)result.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
526             if (bspCompliant) {
527                 BSPEnforcer.checkSamlTokenBSPCompliance(secRef, assertion);
528             }
529             SAMLKeyInfo keyInfo = assertion.getSubjectKeyInfo();
530             if (keyInfo == null) {
531                 throw new WSSecurityException(
532                     WSSecurityException.FAILURE, "invalidSAMLsecurity"
533                 );
534             }
535             X509Certificate[] foundCerts = keyInfo.getCerts();
536             if (foundCerts != null) {
537                 certs = new X509Certificate[]{foundCerts[0]};
538             }
539             secretKey = keyInfo.getSecret();
540             publicKey = keyInfo.getPublicKey();
541             principal = createPrincipalFromSAML(assertion);
542         }
543     }
544     
545     
546 }