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.WSConstants;
23  import org.apache.ws.security.WSDocInfo;
24  import org.apache.ws.security.WSPasswordCallback;
25  import org.apache.ws.security.WSSConfig;
26  import org.apache.ws.security.WSSecurityEngineResult;
27  import org.apache.ws.security.WSSecurityException;
28  import org.apache.ws.security.components.crypto.Crypto;
29  import org.apache.ws.security.handler.RequestData;
30  import org.apache.ws.security.message.token.BinarySecurity;
31  import org.apache.ws.security.message.token.SecurityTokenReference;
32  import org.apache.ws.security.saml.SAMLKeyInfo;
33  import org.apache.ws.security.saml.SAMLUtil;
34  import org.apache.ws.security.saml.ext.AssertionWrapper;
35  import org.apache.ws.security.util.WSSecurityUtil;
36  import org.w3c.dom.Element;
37  
38  import java.security.Principal;
39  import java.security.PublicKey;
40  import java.security.cert.X509Certificate;
41  import java.util.Arrays;
42  import java.util.List;
43  import java.util.Map;
44  
45  import javax.security.auth.callback.Callback;
46  
47  /**
48   * This implementation of STRParser is for parsing a SecurityTokenReference element associated
49   * with a DerivedKeyToken element.
50   */
51  public class DerivedKeyTokenSTRParser implements STRParser {
52      
53      private byte[] secretKey;
54      
55      /**
56       * Parse a SecurityTokenReference element and extract credentials.
57       * 
58       * @param strElement The SecurityTokenReference element
59       * @param data the RequestData associated with the request
60       * @param wsDocInfo The WSDocInfo object to access previous processing results
61       * @param parameters A set of implementation-specific parameters
62       * @throws WSSecurityException
63       */
64      public void parseSecurityTokenReference(
65          Element strElement,
66          RequestData data,
67          WSDocInfo wsDocInfo,
68          Map<String, Object> parameters
69      ) throws WSSecurityException {
70          boolean bspCompliant = true;
71          Crypto crypto = data.getDecCrypto();
72          WSSConfig config = data.getWssConfig();
73  
74          if (config != null) {
75              bspCompliant = config.isWsiBSPCompliant();
76          }
77          
78          SecurityTokenReference secRef = new SecurityTokenReference(strElement, bspCompliant);
79          
80          String uri = null;
81          if (secRef.containsReference()) {
82              uri = secRef.getReference().getURI();
83              if (uri.charAt(0) == '#') {
84                  uri = uri.substring(1);
85              }
86          } else if (secRef.containsKeyIdentifier()) {
87              uri = secRef.getKeyIdentifierValue();
88          }
89          
90          WSSecurityEngineResult result = wsDocInfo.getResult(uri);
91          if (result != null) {
92              processPreviousResult(result, secRef, data, wsDocInfo, bspCompliant);
93          } else if (secRef.containsReference()) { 
94              // Now use the callback and get it
95              secretKey = 
96                  getSecretKeyFromToken(uri, null, WSPasswordCallback.SECURITY_CONTEXT_TOKEN, data);
97              if (secretKey == null) {
98                  throw new WSSecurityException(
99                      WSSecurityException.FAILED_CHECK, "unsupportedKeyId", new Object[] {uri}
100                 );
101             }
102         } else if (secRef.containsKeyIdentifier()) {
103             String keyIdentifierValueType = secRef.getKeyIdentifierValueType();
104             if (WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(keyIdentifierValueType)) {
105                 secretKey = 
106                     getSecretKeyFromToken(
107                         secRef.getKeyIdentifierValue(), keyIdentifierValueType, 
108                         WSPasswordCallback.SECRET_KEY, data
109                     );
110                 if (secretKey == null) {
111                     byte[] keyBytes = secRef.getSKIBytes();
112                     List<WSSecurityEngineResult> resultsList = 
113                         wsDocInfo.getResultsByTag(WSConstants.BST);
114                     for (WSSecurityEngineResult bstResult : resultsList) {
115                         BinarySecurity bstToken = 
116                             (BinarySecurity)bstResult.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
117                         byte[] tokenDigest = WSSecurityUtil.generateDigest(bstToken.getToken());
118                         if (Arrays.equals(tokenDigest, keyBytes)) {
119                             secretKey = (byte[])bstResult.get(WSSecurityEngineResult.TAG_SECRET);
120                             break;
121                         }
122                     }
123                 }
124                 if (secretKey == null) {
125                     throw new WSSecurityException(
126                         WSSecurityException.FAILED_CHECK, "unsupportedKeyId", new Object[] {uri}
127                     );
128                 }
129             } else {
130                 if (bspCompliant 
131                     && keyIdentifierValueType.equals(SecurityTokenReference.ENC_KEY_SHA1_URI)) {
132                     BSPEnforcer.checkEncryptedKeyBSPCompliance(secRef);
133                 }
134                 X509Certificate[] certs = secRef.getKeyIdentifier(crypto);
135                 if (certs == null || certs.length < 1 || certs[0] == null) {
136                     secretKey = 
137                         this.getSecretKeyFromToken(
138                             secRef.getKeyIdentifierValue(), keyIdentifierValueType, 
139                             WSPasswordCallback.SECRET_KEY, data
140                        ); 
141                     if (secretKey == null) {
142                         throw new WSSecurityException(
143                             WSSecurityException.FAILED_CHECK, "unsupportedKeyId", new Object[] {uri}
144                         );
145                     }
146                 } else {
147                     secretKey = crypto.getPrivateKey(certs[0], data.getCallbackHandler()).getEncoded();
148                 }
149             }
150         } else {
151             throw new WSSecurityException(
152                 WSSecurityException.FAILED_CHECK, "unsupportedKeyId"
153             );
154         }
155     }
156     
157     /**
158      * Get the X509Certificates associated with this SecurityTokenReference
159      * @return the X509Certificates associated with this SecurityTokenReference
160      */
161     public X509Certificate[] getCertificates() {
162         return null;
163     }
164     
165     /**
166      * Get the Principal associated with this SecurityTokenReference
167      * @return the Principal associated with this SecurityTokenReference
168      */
169     public Principal getPrincipal() {
170         return null;
171     }
172     
173     /**
174      * Get the PublicKey associated with this SecurityTokenReference
175      * @return the PublicKey associated with this SecurityTokenReference
176      */
177     public PublicKey getPublicKey() {
178         return null;
179     }
180     
181     /**
182      * Get the Secret Key associated with this SecurityTokenReference
183      * @return the Secret Key associated with this SecurityTokenReference
184      */
185     public byte[] getSecretKey() {
186         return secretKey;
187     }
188     
189     /**
190      * Get whether the returned credential is already trusted or not. This is currently
191      * applicable in the case of a credential extracted from a trusted HOK SAML Assertion,
192      * and a BinarySecurityToken that has been processed by a Validator. In these cases,
193      * the SignatureProcessor does not need to verify trust on the credential.
194      * @return true if trust has already been verified on the returned Credential
195      */
196     public boolean isTrustedCredential() {
197         return false;
198     }
199     
200     /**
201      * Get how the certificates were referenced
202      * @return how the certificates were referenced
203      */
204     public REFERENCE_TYPE getCertificatesReferenceType() {
205         return null;
206     }
207 
208     /**
209      * Get the Secret Key from a CallbackHandler
210      * @param id The id of the element
211      * @param type The type of the element (may be null)
212      * @param cb The CallbackHandler object
213      * @return A Secret Key
214      * @throws WSSecurityException
215      */
216     private byte[] getSecretKeyFromToken(
217         String id,
218         String type,
219         int identifier,
220         RequestData data
221     ) throws WSSecurityException {
222         if (id.charAt(0) == '#') {
223             id = id.substring(1);
224         }
225         WSPasswordCallback pwcb = 
226             new WSPasswordCallback(id, null, type, identifier, data);
227         try {
228             Callback[] callbacks = new Callback[]{pwcb};
229             if (data.getCallbackHandler() != null) {
230                 data.getCallbackHandler().handle(callbacks);
231                 return pwcb.getKey();
232             }
233         } catch (Exception e) {
234             throw new WSSecurityException(
235                 WSSecurityException.FAILURE,
236                 "noPassword", 
237                 new Object[] {id}, 
238                 e
239             );
240         }
241 
242         return null;
243     }
244     
245     /**
246      * Process a previous security result
247      */
248     private void processPreviousResult(
249         WSSecurityEngineResult result,
250         SecurityTokenReference secRef,
251         RequestData data,
252         WSDocInfo wsDocInfo,
253         boolean bspCompliant
254     ) throws WSSecurityException {
255         int action = ((Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
256         if (WSConstants.UT_NOPASSWORD == action || WSConstants.UT == action) {
257             if (bspCompliant) {
258                 BSPEnforcer.checkUsernameTokenBSPCompliance(secRef);
259             }
260             secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
261         } else if (WSConstants.ENCR == action) {
262             if (bspCompliant) {
263                 BSPEnforcer.checkEncryptedKeyBSPCompliance(secRef);
264             }
265             secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
266         } else if (WSConstants.SCT == action || WSConstants.BST == action) {
267             secretKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
268         } else if (WSConstants.ST_UNSIGNED == action || WSConstants.ST_SIGNED == action) {
269             AssertionWrapper assertion = 
270                 (AssertionWrapper)result.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
271             if (bspCompliant) {
272                 BSPEnforcer.checkSamlTokenBSPCompliance(secRef, assertion);
273             }
274             SAMLKeyInfo keyInfo = 
275                 SAMLUtil.getCredentialFromSubject(assertion, 
276                                                   data, wsDocInfo, bspCompliant);
277             // TODO Handle malformed SAML tokens where they don't have the 
278             // secret in them
279             secretKey = keyInfo.getSecret();
280         } else {
281             throw new WSSecurityException(
282                 WSSecurityException.FAILED_CHECK, "unsupportedKeyId"
283             );
284         }
285     }
286 }