001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jca;
019
020
021import java.security.NoSuchAlgorithmException;
022import java.security.Provider;
023import java.security.Security;
024import javax.crypto.Cipher;
025import javax.crypto.Mac;
026import javax.crypto.NoSuchPaddingException;
027
028import com.nimbusds.jose.*;
029import com.nimbusds.jose.crypto.impl.ECDSA;
030import com.nimbusds.jose.crypto.impl.RSASSA;
031
032
033/**
034 * Java Cryptography Architecture (JCA) support helper.
035 *
036 * @version 2022-05-16
037 */
038public final class JCASupport {
039
040
041        /**
042         * Checks if unlimited cryptographic strength is supported. If not
043         * download the appropriate jurisdiction policy files for your Java
044         * edition:
045         *
046         * <p><a href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JCE Unlimited Strength Jurisdiction Policy Files for Java 7</a>
047         *
048         * <p><a href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JCE Unlimited Strength Jurisdiction Policy Files for Java 8</a>
049         *
050         * @return {@code true} if unlimited cryptographic strength is
051         *         supported, {@code false} if not.
052         */
053        public static boolean isUnlimitedStrength() {
054
055                try {
056                        return Cipher.getMaxAllowedKeyLength("AES") >= 256;
057                } catch (NoSuchAlgorithmException e) {
058                        return false;
059                }
060        }
061        
062        
063        /**
064         * Checks if the specified JOSE algorithm is supported by the default
065         * system JCA provider(s).
066         *
067         * @param alg The JOSE algorithm. Must not be {@code null}.
068         *
069         * @return {@code true} if the JOSE algorithm is supported, else
070         *         {@code false}.
071         */
072        public static boolean isSupported(final Algorithm alg) {
073                
074                if (alg instanceof JWSAlgorithm) {
075                        return isSupported((JWSAlgorithm)alg);
076                }
077                if (alg instanceof JWEAlgorithm) {
078                        return isSupported((JWEAlgorithm)alg);
079                }
080                if (alg instanceof EncryptionMethod) {
081                        return isSupported((EncryptionMethod)alg);
082                }
083                throw new IllegalArgumentException("Unexpected algorithm class: " + alg.getClass().getCanonicalName());
084        }
085        
086        
087        /**
088         * Checks if a JOSE algorithm is supported by the specified JCA
089         * provider.
090         *
091         * @param alg      The JOSE algorithm. Must not be {@code null}.
092         * @param provider The JCA provider. Must not be {@code null}.
093         *
094         * @return {@code true} if the JOSE algorithm is supported, else
095         *         {@code false}.
096         */
097        public static boolean isSupported(final Algorithm alg, final Provider provider) {
098                
099                if (alg instanceof JWSAlgorithm) {
100                        return isSupported((JWSAlgorithm)alg, provider);
101                }
102                if (alg instanceof JWEAlgorithm) {
103                        return isSupported((JWEAlgorithm)alg, provider);
104                }
105                if (alg instanceof EncryptionMethod) {
106                        return isSupported((EncryptionMethod)alg, provider);
107                }
108                throw new IllegalArgumentException("Unexpected algorithm class: " + alg.getClass().getCanonicalName());
109        }
110
111
112        /**
113         * Checks if the specified JWS algorithm is supported by the default
114         * system JCA provider(s).
115         *
116         * @param alg The JWS algorithm. Must not be {@code null}.
117         *
118         * @return {@code true} if the JWS algorithm is supported, else
119         *         {@code false}.
120         */
121        public static boolean isSupported(final JWSAlgorithm alg) {
122                
123                if (alg.getName().equals(Algorithm.NONE.getName())) {
124                        return true;
125                }
126
127                for (Provider p: Security.getProviders()) {
128
129                        if (isSupported(alg, p)) {
130                                return true;
131                        }
132                }
133
134                return false;
135        }
136
137
138        /**
139         * Checks if a JWS algorithm is supported by the specified JCA
140         * provider.
141         *
142         * @param alg      The JWS algorithm. Must not be {@code null}.
143         * @param provider The JCA provider. Must not be {@code null}.
144         *
145         * @return {@code true} if the JWS algorithm is supported, else
146         *         {@code false}.
147         */
148        public static boolean isSupported(final JWSAlgorithm alg, final Provider provider) {
149
150                if (JWSAlgorithm.Family.HMAC_SHA.contains(alg)) {
151                        String jcaName;
152                        if (alg.equals(JWSAlgorithm.HS256)) {
153                                jcaName = "HMACSHA256";
154                        } else if (alg.equals(JWSAlgorithm.HS384)) {
155                                jcaName = "HMACSHA384";
156                        } else if (alg.equals(JWSAlgorithm.HS512)) {
157                                jcaName = "HMACSHA512";
158                        } else {
159                                return false;
160                        }
161                        try {
162                                Mac.getInstance(jcaName, provider);
163                        } catch (NoSuchAlgorithmException e) {
164                                return false;
165                        }
166                        return true;
167                }
168
169                if (JWSAlgorithm.Family.RSA.contains(alg)) {
170                        try {
171                                RSASSA.getSignerAndVerifier(alg, provider);
172                        } catch (JOSEException e) {
173                                return false;
174                        }
175                        return true;
176                }
177
178                if (JWSAlgorithm.Family.EC.contains(alg)) {
179                        try {
180                                ECDSA.getSignerAndVerifier(alg, provider);
181                        } catch (JOSEException e) {
182                                return false;
183                        }
184                        return true;
185                }
186
187                return false;
188        }
189
190
191        /**
192         * Checks if the specified JWE algorithm is supported by the default
193         * system JCA provider(s).
194         *
195         * @param alg The JWE algorithm. Must not be {@code null}.
196         *
197         * @return {@code true} if the JWE algorithm is supported, else
198         *         {@code false}.
199         */
200        public static boolean isSupported(final JWEAlgorithm alg) {
201
202                for (Provider p: Security.getProviders()) {
203
204                        if (isSupported(alg, p)) {
205                                return true;
206                        }
207                }
208
209                return false;
210        }
211
212
213        /**
214         * Checks if a JWE algorithm is supported by the specified JCA
215         * provider.
216         *
217         * @param alg      The JWE algorithm. Must not be {@code null}.
218         * @param provider The JCA provider. Must not be {@code null}.
219         *
220         * @return {@code true} if the JWE algorithm is supported, else
221         *         {@code false}.
222         */
223        public static boolean isSupported(final JWEAlgorithm alg, final Provider provider) {
224
225                String jcaName;
226
227                if (JWEAlgorithm.Family.RSA.contains(alg)) {
228                        if (alg.equals(JWEAlgorithm.RSA1_5)) {
229                                jcaName = "RSA/ECB/PKCS1Padding";
230                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
231                                jcaName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
232                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
233                                jcaName = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
234                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP_512)) {
235                                jcaName = "RSA/ECB/OAEPWithSHA-512AndMGF1Padding";
236                        } else {
237                                return false;
238                        }
239
240                        // Do direct test
241                        try {
242                                Cipher.getInstance(jcaName, provider);
243                        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
244                                return false;
245                        }
246                        return true;
247                }
248
249                if (JWEAlgorithm.Family.AES_KW.contains(alg)) {
250                        return provider.getService("Cipher", "AESWrap") != null;
251                }
252
253                if (JWEAlgorithm.Family.ECDH_ES.contains(alg)) {
254                        return provider.getService("KeyAgreement", "ECDH") != null;
255                }
256
257                if (JWEAlgorithm.Family.AES_GCM_KW.contains(alg)) {
258                        // Do direct test
259                        try {
260                                Cipher.getInstance("AES/GCM/NoPadding", provider);
261                        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
262                                return false;
263                        }
264                        return true;
265                }
266
267                if (JWEAlgorithm.Family.PBES2.contains(alg)) {
268                        String hmac;
269                        if (alg.equals(JWEAlgorithm.PBES2_HS256_A128KW)) {
270                                hmac = "HmacSHA256";
271                        } else if (alg.equals(JWEAlgorithm.PBES2_HS384_A192KW)) {
272                                hmac = "HmacSHA384";
273                        } else {
274                                hmac = "HmacSHA512";
275                        }
276                        return provider.getService("KeyGenerator", hmac) != null;
277                }
278                
279                return JWEAlgorithm.DIR.equals(alg); // Always supported
280        }
281
282
283        /**
284         * Checks if the specified JWE encryption method is supported by the
285         * default system JCA provider(s).
286         *
287         * @param enc The JWE encryption method. Must not be {@code null}.
288         *
289         * @return {@code true} if the JWE algorithm is supported, else
290         *         {@code false}.
291         */
292        public static boolean isSupported(final EncryptionMethod enc) {
293
294                for (Provider p: Security.getProviders()) {
295
296                        if (isSupported(enc, p)) {
297                                return true;
298                        }
299                }
300
301                return false;
302        }
303
304
305        /**
306         * Checks if a JWE encryption method is supported by the specified
307         * JCA provider.
308         *
309         * @param enc      The JWE encryption method. Must not be {@code null}.
310         * @param provider The JCA provider. Must not be {@code null}.
311         *
312         * @return {@code true} if the JWE encryption method is supported, else
313         *         {@code false}.
314         */
315        public static boolean isSupported(final EncryptionMethod enc, final Provider provider) {
316
317                if (EncryptionMethod.Family.AES_CBC_HMAC_SHA.contains(enc)) {
318                        // Do direct test
319                        try {
320                                Cipher.getInstance("AES/CBC/PKCS5Padding", provider);
321                        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
322                                return false;
323                        }
324                        // Check hmac
325                        String hmac;
326                        if (enc.equals(EncryptionMethod.A128CBC_HS256)) {
327                                hmac = "HmacSHA256";
328                        } else if (enc.equals(EncryptionMethod.A192CBC_HS384)) {
329                                hmac = "HmacSHA384";
330                        } else {
331                                hmac = "HmacSHA512";
332                        }
333                        return provider.getService("KeyGenerator", hmac) != null;
334                }
335
336                if (EncryptionMethod.Family.AES_GCM.contains(enc)) {
337                        // Do direct test
338                        try {
339                                Cipher.getInstance("AES/GCM/NoPadding", provider);
340                        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
341                                return false;
342                        }
343                        return true;
344                }
345
346                return false;
347        }
348
349
350        /**
351         * Prevents public instantiation.
352         */
353        private JCASupport() {
354
355        }
356}