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.common.crypto;
21  
22  import java.security.PublicKey;
23  import java.security.cert.X509Certificate;
24  import java.security.interfaces.DSAPublicKey;
25  import java.security.interfaces.ECPublicKey;
26  import java.security.interfaces.RSAPublicKey;
27  import java.util.Set;
28  
29  import javax.xml.crypto.dsig.Reference;
30  import javax.xml.crypto.dsig.Transform;
31  import javax.xml.crypto.dsig.XMLSignature;
32  
33  import org.apache.wss4j.common.ext.WSSecurityException;
34  
35  /**
36   * Validate signature/encryption/etc. algorithms against an AlgorithmSuite policy.
37   */
38  public class AlgorithmSuiteValidator {
39  
40      private static final org.slf4j.Logger LOG =
41          org.slf4j.LoggerFactory.getLogger(AlgorithmSuiteValidator.class);
42  
43      private final AlgorithmSuite algorithmSuite;
44  
45      public AlgorithmSuiteValidator(
46          AlgorithmSuite algorithmSuite
47      ) {
48          this.algorithmSuite = algorithmSuite;
49      }
50  
51      /**
52       * Check the Signature Method
53       */
54      public void checkSignatureMethod(
55          String signatureMethod
56      ) throws WSSecurityException {
57          Set<String> allowedSignatureMethods = algorithmSuite.getSignatureMethods();
58          if (!allowedSignatureMethods.isEmpty()
59              && !allowedSignatureMethods.contains(signatureMethod)) {
60              LOG.warn(
61                  "SignatureMethod " + signatureMethod + " does not match required values"
62              );
63              throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
64          }
65      }
66  
67      /**
68       * Check the C14n Algorithm
69       */
70      public void checkC14nAlgorithm(
71          String c14nAlgorithm
72      ) throws WSSecurityException {
73          Set<String> allowedC14nAlgorithms = algorithmSuite.getC14nAlgorithms();
74          if (!allowedC14nAlgorithms.isEmpty() && !allowedC14nAlgorithms.contains(c14nAlgorithm)) {
75              LOG.warn(
76                  "C14nMethod " + c14nAlgorithm + " does not match required value"
77              );
78              throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
79          }
80      }
81  
82      /**
83       * Check the Signature Algorithms
84       */
85      public void checkSignatureAlgorithms(
86          XMLSignature xmlSignature
87      ) throws WSSecurityException {
88          // Signature Algorithm
89          String signatureMethod =
90              xmlSignature.getSignedInfo().getSignatureMethod().getAlgorithm();
91          checkSignatureMethod(signatureMethod);
92  
93          // C14n Algorithm
94          String c14nMethod =
95              xmlSignature.getSignedInfo().getCanonicalizationMethod().getAlgorithm();
96          checkC14nAlgorithm(c14nMethod);
97  
98          for (Object refObject : xmlSignature.getSignedInfo().getReferences()) {
99              Reference reference = (Reference)refObject;
100             // Digest Algorithm
101             String digestMethod = reference.getDigestMethod().getAlgorithm();
102             Set<String> allowedDigestAlgorithms = algorithmSuite.getDigestAlgorithms();
103             if (!allowedDigestAlgorithms.isEmpty()
104                     && !allowedDigestAlgorithms.contains(digestMethod)) {
105                 LOG.warn(
106                     "DigestMethod " + digestMethod + " does not match required value"
107                 );
108                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
109             }
110 
111             // Transform Algorithms
112             for (int i = 0; i < reference.getTransforms().size(); i++) {
113                 Transform transform = (Transform)reference.getTransforms().get(i);
114                 String algorithm = transform.getAlgorithm();
115                 Set<String> allowedTransformAlgorithms =
116                         algorithmSuite.getTransformAlgorithms();
117                 if (!allowedTransformAlgorithms.isEmpty()
118                         && !allowedTransformAlgorithms.contains(algorithm)) {
119                     LOG.warn(
120                         "Transform method " + algorithm + " does not match required value"
121                     );
122                     throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
123                 }
124             }
125         }
126     }
127 
128     public void checkEncryptionKeyWrapAlgorithm(
129         String keyWrapAlgorithm
130     ) throws WSSecurityException {
131         Set<String> keyWrapAlgorithms = algorithmSuite.getKeyWrapAlgorithms();
132         if (!keyWrapAlgorithms.isEmpty()
133             && !keyWrapAlgorithms.contains(keyWrapAlgorithm)) {
134             LOG.warn(
135                 "The Key transport method does not match the requirement"
136             );
137             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
138         }
139     }
140 
141     public void checkSymmetricEncryptionAlgorithm(
142         String symmetricAlgorithm
143     ) throws WSSecurityException {
144         Set<String> encryptionMethods = algorithmSuite.getEncryptionMethods();
145         if (!encryptionMethods.isEmpty()
146             && !encryptionMethods.contains(symmetricAlgorithm)) {
147             LOG.warn(
148                 "The encryption algorithm does not match the requirement"
149             );
150             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
151         }
152     }
153 
154     /**
155      * Check the asymmetric key length
156      */
157     public void checkAsymmetricKeyLength(
158         X509Certificate[] x509Certificates
159     ) throws WSSecurityException {
160         if (x509Certificates == null) {
161             return;
162         }
163 
164         for (X509Certificate cert : x509Certificates) {
165             checkAsymmetricKeyLength(cert.getPublicKey());
166         }
167     }
168 
169     /**
170      * Check the asymmetric key length
171      */
172     public void checkAsymmetricKeyLength(
173         X509Certificate x509Certificate
174     ) throws WSSecurityException {
175         if (x509Certificate == null) {
176             return;
177         }
178 
179         checkAsymmetricKeyLength(x509Certificate.getPublicKey());
180     }
181 
182     /**
183      * Check the asymmetric key length
184      */
185     public void checkAsymmetricKeyLength(
186         PublicKey publicKey
187     ) throws WSSecurityException {
188         if (publicKey == null) {
189             return;
190         }
191         if (publicKey instanceof RSAPublicKey) {
192             int modulus = ((RSAPublicKey)publicKey).getModulus().bitLength();
193             if (modulus < algorithmSuite.getMinimumAsymmetricKeyLength()
194                 || modulus > algorithmSuite.getMaximumAsymmetricKeyLength()) {
195                 LOG.warn(
196                     "The asymmetric key length does not match the requirement"
197                 );
198                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
199             }
200         } else if (publicKey instanceof DSAPublicKey) {
201             int length = ((DSAPublicKey)publicKey).getParams().getP().bitLength();
202             if (length < algorithmSuite.getMinimumAsymmetricKeyLength()
203                 || length > algorithmSuite.getMaximumAsymmetricKeyLength()) {
204                 LOG.warn(
205                     "The asymmetric key length does not match the requirement"
206                 );
207                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
208             }
209         } else if (publicKey instanceof ECPublicKey) {
210             final ECPublicKey ecpriv = (ECPublicKey) publicKey;
211             final java.security.spec.ECParameterSpec spec = ecpriv.getParams();
212             int length = spec.getOrder().bitLength();
213             if (length < algorithmSuite.getMinimumEllipticCurveKeyLength()
214                     || length > algorithmSuite.getMaximumEllipticCurveKeyLength()) {
215                 LOG.warn("The elliptic curve key length does not match the requirement");
216                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
217             }
218         } else {
219             LOG.warn(
220                 "An unknown public key was provided"
221             );
222             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
223         }
224     }
225 
226     /**
227      * Check the symmetric key length
228      */
229     public void checkSymmetricKeyLength(
230         int secretKeyLength
231     ) throws WSSecurityException {
232         if (secretKeyLength < (algorithmSuite.getMinimumSymmetricKeyLength() / 8)
233             || secretKeyLength > (algorithmSuite.getMaximumSymmetricKeyLength() / 8)) {
234             LOG.warn(
235                 "The symmetric key length does not match the requirement"
236             );
237             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
238         }
239     }
240 
241     /**
242      * Check Signature Derived Key length (in bytes)
243      */
244     public void checkSignatureDerivedKeyLength(
245         int derivedKeyLength
246     ) throws WSSecurityException {
247         int requiredKeyLength = algorithmSuite.getSignatureDerivedKeyLength();
248         if (requiredKeyLength > 0 && (derivedKeyLength / 8) != requiredKeyLength) {
249             LOG.warn(
250                 "The signature derived key length of " + derivedKeyLength + " does not match"
251                 + " the requirement of " + requiredKeyLength
252             );
253         }
254     }
255 
256     /**
257      * Check Encryption Derived Key length (in bytes)
258      */
259     public void checkEncryptionDerivedKeyLength(
260         int derivedKeyLength
261     ) throws WSSecurityException {
262         int requiredKeyLength = algorithmSuite.getEncryptionDerivedKeyLength();
263         if (requiredKeyLength > 0 && (derivedKeyLength / 8) != requiredKeyLength) {
264             LOG.warn(
265                 "The encryption derived key length of " + derivedKeyLength + " does not match"
266                 + " the requirement of " + requiredKeyLength
267             );
268         }
269     }
270 
271     /**
272      * Check Derived Key algorithm
273      */
274     public void checkDerivedKeyAlgorithm(
275         String algorithm
276     ) throws WSSecurityException {
277         Set<String> derivedKeyAlgorithms = algorithmSuite.getDerivedKeyAlgorithms();
278         if (!derivedKeyAlgorithms.isEmpty()
279             && !derivedKeyAlgorithms.contains(algorithm)) {
280             LOG.warn(
281                 "The Derived Key Algorithm does not match the requirement"
282             );
283             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
284         }
285     }
286 
287 }