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.message;
21  
22  import java.nio.charset.StandardCharsets;
23  import java.security.cert.X509Certificate;
24  import java.util.Arrays;
25  
26  import javax.crypto.SecretKey;
27  
28  import org.apache.wss4j.common.crypto.Crypto;
29  import org.apache.wss4j.common.crypto.CryptoType;
30  import org.apache.wss4j.common.derivedKey.AlgoFactory;
31  import org.apache.wss4j.common.derivedKey.ConversationConstants;
32  import org.apache.wss4j.common.derivedKey.DerivationAlgorithm;
33  import org.apache.wss4j.common.ext.WSSecurityException;
34  import org.apache.wss4j.common.token.Reference;
35  import org.apache.wss4j.common.token.SecurityTokenReference;
36  import org.apache.wss4j.common.util.KeyUtils;
37  import org.apache.wss4j.common.util.UsernameTokenUtil;
38  import org.apache.wss4j.dom.WSConstants;
39  import org.apache.wss4j.dom.message.token.DerivedKeyToken;
40  import org.apache.wss4j.dom.message.token.KerberosSecurity;
41  import org.apache.wss4j.dom.util.WSSecurityUtil;
42  import org.apache.xml.security.utils.XMLUtils;
43  import org.w3c.dom.Document;
44  import org.w3c.dom.Element;
45  
46  /**
47   * Base class for DerivedKey encryption and signature
48   */
49  public abstract class WSSecDerivedKeyBase extends WSSecSignatureBase {
50  
51      private static final org.slf4j.Logger LOG =
52          org.slf4j.LoggerFactory.getLogger(WSSecDerivedKeyBase.class);
53  
54      /**
55       * DerivedKeyToken of this builder
56       */
57      private DerivedKeyToken dkt;
58  
59      /**
60       * Client's label value
61       */
62      private String clientLabel = ConversationConstants.DEFAULT_LABEL;
63  
64      /**
65       * Service's label value
66       */
67      private String serviceLabel = ConversationConstants.DEFAULT_LABEL;
68  
69      /**
70       * The Token identifier of the token that the <code>DerivedKeyToken</code>
71       * is (or to be) derived from.
72       */
73      private String tokenIdentifier;
74  
75      /**
76       * True if the tokenIdentifier is a direct reference to a key identifier
77       * instead of a URI to a key
78       */
79      private boolean tokenIdDirectId;
80  
81      /**
82       * The wsse:SecurityTokenReference element to be used
83       */
84      private Element strElem;
85  
86      /**
87       * wsu:Id of the wsc:DerivedKeyToken
88       */
89      private String dktId;
90  
91      /**
92       * Raw bytes of the derived key
93       */
94      private byte[] derivedKeyBytes;
95  
96      private int wscVersion = ConversationConstants.DEFAULT_VERSION;
97  
98      private String customValueType;
99      private X509Certificate useThisCert;
100     private Crypto crypto;
101 
102     public WSSecDerivedKeyBase(WSSecHeader securityHeader) {
103         super(securityHeader);
104         setKeyIdentifierType(0);
105     }
106 
107     public WSSecDerivedKeyBase(Document doc) {
108         super(doc);
109         setKeyIdentifierType(0);
110     }
111 
112     /**
113      * The derived key will change depending on the sig/encr algorithm.
114      * Therefore the child classes are expected to provide this value.
115      * @return the derived key length
116      * @throws WSSecurityException
117      */
118     protected abstract int getDerivedKeyLength() throws WSSecurityException;
119 
120     public Element getStrElem() {
121         return strElem;
122     }
123 
124     public void setStrElem(Element strElem) {
125         this.strElem = strElem;
126     }
127 
128     public void setTokenIdentifier(String tokenIdentifier) {
129         this.tokenIdentifier = tokenIdentifier;
130     }
131 
132     /**
133      * @return Returns the tokenIdentifier.
134      */
135     public String getTokenIdentifier() {
136         return tokenIdentifier;
137     }
138 
139     /**
140      * Set the X509 Certificate to use
141      * @param cer the X509 Certificate to use
142      */
143     public void setX509Certificate(X509Certificate cer) {
144         this.useThisCert = cer;
145     }
146 
147     /**
148      * Get the id generated during <code>prepare()</code>.
149      *
150      * Returns the the value of wsu:Id attribute of the DerivedKeyToken element.
151      *
152      * @return Return the wsu:Id of this token or null if <code>prepare()</code>
153      *         was not called before.
154      */
155     public String getId() {
156         return dktId;
157     }
158 
159     /**
160      * Set the label value of the client.
161      * @param clientLabel
162      */
163     public void setClientLabel(String clientLabel) {
164         this.clientLabel = clientLabel;
165     }
166 
167     /**
168      * Set the label value of the service.
169      * @param serviceLabel
170      */
171     public void setServiceLabel(String serviceLabel) {
172         this.serviceLabel = serviceLabel;
173     }
174 
175     /**
176      * Initialize a WSSec Derived key.
177      *
178      * The method prepares and initializes a WSSec derived key structure after the
179      * relevant information was set. This method also creates and initializes the
180      * derived token using the ephemeral key. After preparation references
181      * can be added, encrypted and signed as required.
182      *
183      * This method does not add any element to the security header. This must be
184      * done explicitly.
185      *
186      * @param ephemeralKey The ephemeral key to use for derivation
187      * @throws WSSecurityException
188      */
189     public void prepare(byte[] ephemeralKey) throws WSSecurityException {
190 
191         if (ephemeralKey == null || ephemeralKey.length == 0) {
192             LOG.debug("No ephemeral key is supplied for id: " + tokenIdentifier);
193             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
194         }
195 
196         // Create the derived keys
197         // At this point figure out the key length according to the symencAlgo
198         int offset = 0;
199         int length = getDerivedKeyLength();
200         byte[] label;
201         String labelText = clientLabel + serviceLabel;
202         label = labelText.getBytes(StandardCharsets.UTF_8);
203         byte[] nonce = UsernameTokenUtil.generateNonce(16);
204 
205         byte[] seed = new byte[label.length + nonce.length];
206         System.arraycopy(label, 0, seed, 0, label.length);
207         System.arraycopy(nonce, 0, seed, label.length, nonce.length);
208 
209         DerivationAlgorithm algo =
210             AlgoFactory.getInstance(ConversationConstants.DerivationAlgorithm.P_SHA_1);
211         derivedKeyBytes = algo.createKey(ephemeralKey, seed, offset, length);
212 
213         // Add the DKTs
214         dkt = new DerivedKeyToken(wscVersion, getDocument());
215         dktId = getIdAllocator().createId("DK-", dkt);
216 
217         dkt.setOffset(offset);
218         dkt.setLength(length);
219         dkt.setNonce(XMLUtils.encodeToString(nonce));
220         dkt.setID(dktId);
221         if (addWSUNamespace) {
222             dkt.addWSUNamespace();
223         }
224 
225         if (strElem == null) {
226             SecurityTokenReference secRef = new SecurityTokenReference(getDocument());
227             String strUri = getIdAllocator().createSecureId("STR-", secRef);
228             secRef.setID(strUri);
229             if (addWSUNamespace) {
230                 secRef.addWSUNamespace();
231             }
232 
233             X509Certificate[] certs = getSigningCerts();
234 
235                 switch (keyIdentifierType) {
236 
237             case WSConstants.X509_KEY_IDENTIFIER:
238                 secRef.setKeyIdentifier(certs[0]);
239                 break;
240 
241             case WSConstants.SKI_KEY_IDENTIFIER:
242                 secRef.setKeyIdentifierSKI(certs[0], crypto);
243                 break;
244 
245             case WSConstants.THUMBPRINT_IDENTIFIER:
246                 secRef.setKeyIdentifierThumb(certs[0]);
247                 break;
248 
249             case WSConstants.CUSTOM_KEY_IDENTIFIER:
250                 secRef.setKeyIdentifier(customValueType, tokenIdentifier);
251                 if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(customValueType)) {
252                     secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
253                 } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customValueType)) {
254                     secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
255                 } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customValueType)) {
256                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
257                 }
258                 break;
259             default:
260                 Reference ref = new Reference(getDocument());
261 
262                 if (tokenIdDirectId) {
263                     ref.setURI(tokenIdentifier);
264                 } else {
265                     ref.setURI("#" + tokenIdentifier);
266                 }
267                 if (customValueType != null && customValueType.length() != 0) {
268                     ref.setValueType(customValueType);
269                 }
270                 if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(customValueType)) {
271                     secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
272                     ref.setValueType(customValueType);
273                 } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customValueType)) {
274                     secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
275                 } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customValueType)) {
276                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
277                     ref.setValueType(customValueType);
278                 } else if (KerberosSecurity.isKerberosToken(customValueType)) {
279                     secRef.addTokenType(customValueType);
280                     ref.setValueType(customValueType);
281                 } else if (WSConstants.WSC_SCT.equals(customValueType)
282                     || WSConstants.WSC_SCT_05_12.equals(customValueType)) {
283                     ref.setValueType(customValueType);
284                 } else if (!WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE.equals(customValueType)) {
285                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
286                 }
287 
288                 secRef.setReference(ref);
289                 break;
290             }
291 
292             dkt.setSecurityTokenReference(secRef);
293         } else {
294             dkt.setSecurityTokenReference(strElem);
295         }
296     }
297 
298 
299     /**
300      * Prepend the DerivedKey element to the elements already in the Security
301      * header.
302      *
303      * The method can be called any time after <code>prepare()</code>. This
304      * allows to insert the DerivedKey element at any position in the Security
305      * header.
306      */
307     public void prependDKElementToHeader() {
308         Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
309         WSSecurityUtil.prependChildElement(securityHeaderElement, dkt.getElement());
310     }
311 
312     public void appendDKElementToHeader() {
313         Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
314         securityHeaderElement.appendChild(dkt.getElement());
315     }
316 
317     /**
318      * @param wscVersion The wscVersion to set.
319      */
320     public void setWscVersion(int wscVersion) {
321         this.wscVersion = wscVersion;
322     }
323 
324     public int getWscVersion() {
325         return wscVersion;
326     }
327 
328     public Element getdktElement() {
329         return dkt.getElement();
330     }
331 
332     public void setCustomValueType(String customValueType) {
333         this.customValueType = customValueType;
334     }
335 
336     public void setTokenIdDirectId(boolean b) {
337         tokenIdDirectId = b;
338     }
339 
340     /**
341      * Set up the X509 Certificate(s) for signing.
342      */
343     private X509Certificate[] getSigningCerts() throws WSSecurityException {
344         X509Certificate[] certs = null;
345         if (keyIdentifierType == WSConstants.ISSUER_SERIAL
346             || keyIdentifierType == WSConstants.ISSUER_SERIAL_QUOTE_FORMAT
347             || keyIdentifierType == WSConstants.X509_KEY_IDENTIFIER
348             || keyIdentifierType == WSConstants.SKI_KEY_IDENTIFIER
349             || keyIdentifierType == WSConstants.THUMBPRINT_IDENTIFIER) {
350             if (useThisCert == null) {
351                 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
352                 cryptoType.setAlias(user);
353                 if (crypto == null) {
354                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noSigCryptoFile");
355                 }
356                 certs = crypto.getX509Certificates(cryptoType);
357             } else {
358                 certs = new X509Certificate[] {useThisCert};
359             }
360             if (certs == null || certs.length <= 0) {
361                 throw new WSSecurityException(
362                         WSSecurityException.ErrorCode.FAILURE,
363                         "noUserCertsFound",
364                         new Object[] {user, "signature"});
365             }
366         }
367         return certs;
368     }
369 
370     public void setCrypto(Crypto crypto) {
371         this.crypto = crypto;
372     }
373 
374     protected SecretKey getDerivedKey(String algorithm) {
375         return KeyUtils.prepareSecretKey(algorithm, derivedKeyBytes);
376     }
377 
378     @Override
379     public void clean() {
380         super.clean();
381         if (derivedKeyBytes != null) {
382             Arrays.fill(derivedKeyBytes, (byte)0);
383         }
384     }
385 }