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.NoSuchPaddingException;
026
027import com.nimbusds.jose.Algorithm;
028import com.nimbusds.jose.EncryptionMethod;
029import com.nimbusds.jose.JWEAlgorithm;
030import com.nimbusds.jose.JWSAlgorithm;
031
032
033/**
034 * Java Cryptography Architecture (JCA) support helper.
035 */
036public final class JCASupport {
037
038
039        /**
040         * Checks if unlimited cryptographic strength is supported. If not
041         * download the appropriate jurisdiction policy files for your Java
042         * edition:
043         *
044         * <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>
045         *
046         * <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>
047         *
048         * @return {@code true} if unlimited cryptographic strength is
049         *         supported, {@code false} if not.
050         */
051        public static boolean isUnlimitedStrength() {
052
053                try {
054                        return Cipher.getMaxAllowedKeyLength("AES") >= 256;
055                } catch (NoSuchAlgorithmException e) {
056                        return false;
057                }
058        }
059        
060        
061        /**
062         * Checks if the specified JOSE algorithm is supported by the default
063         * system JCA provider(s).
064         *
065         * @param alg The JOSE algorithm. Must not be {@code null}.
066         *
067         * @return {@code true} if the JOSE algorithm is supported, else
068         *         {@code false}.
069         */
070        public static boolean isSupported(final Algorithm alg) {
071                
072                if (alg instanceof JWSAlgorithm) {
073                        return isSupported((JWSAlgorithm)alg);
074                }
075                if (alg instanceof JWEAlgorithm) {
076                        return isSupported((JWEAlgorithm)alg);
077                }
078                if (alg instanceof EncryptionMethod) {
079                        return isSupported((EncryptionMethod)alg);
080                }
081                throw new IllegalArgumentException("Unexpected algorithm class: " + alg.getClass().getCanonicalName());
082        }
083        
084        
085        /**
086         * Checks if a JOSE algorithm is supported by the the specified JCA
087         * provider.
088         *
089         * @param alg      The JOSE algorithm. Must not be {@code null}.
090         * @param provider The JCA provider. Must not be {@code null}.
091         *
092         * @return {@code true} if the JOSE algorithm is supported, else
093         *         {@code false}.
094         */
095        public static boolean isSupported(final Algorithm alg, final Provider provider) {
096                
097                if (alg instanceof JWSAlgorithm) {
098                        return isSupported((JWSAlgorithm)alg, provider);
099                }
100                if (alg instanceof JWEAlgorithm) {
101                        return isSupported((JWEAlgorithm)alg, provider);
102                }
103                if (alg instanceof EncryptionMethod) {
104                        return isSupported((EncryptionMethod)alg, provider);
105                }
106                throw new IllegalArgumentException("Unexpected algorithm class: " + alg.getClass().getCanonicalName());
107        }
108
109
110        /**
111         * Checks if the specified JWS algorithm is supported by the default
112         * system JCA provider(s).
113         *
114         * @param alg The JWS algorithm. Must not be {@code null}.
115         *
116         * @return {@code true} if the JWS algorithm is supported, else
117         *         {@code false}.
118         */
119        public static boolean isSupported(final JWSAlgorithm alg) {
120                
121                if (alg.getName().equals(Algorithm.NONE.getName())) {
122                        return true;
123                }
124
125                for (Provider p: Security.getProviders()) {
126
127                        if (isSupported(alg, p)) {
128                                return true;
129                        }
130                }
131
132                return false;
133        }
134
135
136        /**
137         * Checks if a JWS algorithm is supported by the the specified JCA
138         * provider.
139         *
140         * @param alg      The JWS algorithm. Must not be {@code null}.
141         * @param provider The JCA provider. Must not be {@code null}.
142         *
143         * @return {@code true} if the JWS algorithm is supported, else
144         *         {@code false}.
145         */
146        public static boolean isSupported(final JWSAlgorithm alg, final Provider provider) {
147
148                if (JWSAlgorithm.Family.HMAC_SHA.contains(alg)) {
149                        String jcaName;
150                        if (alg.equals(JWSAlgorithm.HS256)) {
151                                jcaName = "HMACSHA256";
152                        } else if (alg.equals(JWSAlgorithm.HS384)) {
153                                jcaName = "HMACSHA384";
154                        } else if (alg.equals(JWSAlgorithm.HS512)) {
155                                jcaName = "HMACSHA512";
156                        } else {
157                                return false;
158                        }
159                        return provider.getService("KeyGenerator", jcaName) != null;
160                }
161
162                if (JWSAlgorithm.Family.RSA.contains(alg)) {
163                        String jcaName;
164                        if (alg.equals(JWSAlgorithm.RS256)) {
165                                jcaName = "SHA256withRSA";
166                        } else if (alg.equals(JWSAlgorithm.RS384)) {
167                                jcaName = "SHA384withRSA";
168                        } else if (alg.equals(JWSAlgorithm.RS512)) {
169                                jcaName = "SHA512withRSA";
170                        } else if (alg.equals(JWSAlgorithm.PS256)) {
171                                jcaName = "SHA256withRSAandMGF1";
172                        } else if (alg.equals(JWSAlgorithm.PS384)) {
173                                jcaName = "SHA384withRSAandMGF1";
174                        } else if (alg.equals(JWSAlgorithm.PS512)) {
175                                jcaName = "SHA512withRSAandMGF1";
176                        } else {
177                                return false;
178                        }
179                        return provider.getService("Signature", jcaName) != null;
180                }
181
182                if (JWSAlgorithm.Family.EC.contains(alg)) {
183                        String jcaName;
184                        if (alg.equals(JWSAlgorithm.ES256)) {
185                                jcaName = "SHA256withECDSA";
186                        } else if (alg.equals(JWSAlgorithm.ES384)) {
187                                jcaName = "SHA384withECDSA";
188                        } else if (alg.equals(JWSAlgorithm.ES512)) {
189                                jcaName = "SHA512withECDSA";
190                        } else {
191                                return false;
192                        }
193                        return provider.getService("Signature", jcaName) != null;
194                }
195
196                return false;
197        }
198
199
200        /**
201         * Checks if the specified JWE algorithm is supported by the default
202         * system JCA provider(s).
203         *
204         * @param alg The JWE algorithm. Must not be {@code null}.
205         *
206         * @return {@code true} if the JWE algorithm is supported, else
207         *         {@code false}.
208         */
209        public static boolean isSupported(final JWEAlgorithm alg) {
210
211                for (Provider p: Security.getProviders()) {
212
213                        if (isSupported(alg, p)) {
214                                return true;
215                        }
216                }
217
218                return false;
219        }
220
221
222        /**
223         * Checks if a JWE algorithm is supported by the the specified JCA
224         * provider.
225         *
226         * @param alg      The JWE algorithm. Must not be {@code null}.
227         * @param provider The JCA provider. Must not be {@code null}.
228         *
229         * @return {@code true} if the JWE algorithm is supported, else
230         *         {@code false}.
231         */
232        public static boolean isSupported(final JWEAlgorithm alg, final Provider provider) {
233
234                String jcaName;
235
236                if (JWEAlgorithm.Family.RSA.contains(alg)) {
237                        if (alg.equals(JWEAlgorithm.RSA1_5)) {
238                                jcaName = "RSA/ECB/PKCS1Padding";
239                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
240                                jcaName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
241                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
242                                jcaName = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
243                        } else {
244                                return false;
245                        }
246
247                        // Do direct test
248                        try {
249                                Cipher.getInstance(jcaName, provider);
250                        } catch (NoSuchAlgorithmException e) {
251                                return false;
252                        } catch (NoSuchPaddingException e) {
253                                return false;
254                        }
255                        return true;
256                }
257
258                if (JWEAlgorithm.Family.AES_KW.contains(alg)) {
259                        return provider.getService("Cipher", "AESWrap") != null;
260                }
261
262                if (JWEAlgorithm.Family.ECDH_ES.contains(alg)) {
263                        return provider.getService("KeyAgreement", "ECDH") != null;
264                }
265
266                if (JWEAlgorithm.Family.AES_GCM_KW.contains(alg)) {
267                        // Do direct test
268                        try {
269                                Cipher.getInstance("AES/GCM/NoPadding", provider);
270                        } catch (NoSuchAlgorithmException e) {
271                                return false;
272                        } catch (NoSuchPaddingException e) {
273                                return false;
274                        }
275                        return true;
276                }
277
278                if (JWEAlgorithm.Family.PBES2.contains(alg)) {
279                        String hmac;
280                        if (alg.equals(JWEAlgorithm.PBES2_HS256_A128KW)) {
281                                hmac = "HmacSHA256";
282                        } else if (alg.equals(JWEAlgorithm.PBES2_HS384_A192KW)) {
283                                hmac = "HmacSHA384";
284                        } else {
285                                hmac = "HmacSHA512";
286                        }
287                        return provider.getService("KeyGenerator", hmac) != null;
288                }
289
290                if (JWEAlgorithm.DIR.equals(alg)) {
291                        return true; // Always supported
292                }
293
294                return false;
295        }
296
297
298        /**
299         * Checks if the specified JWE encryption method is supported by the
300         * default system JCA provider(s).
301         *
302         * @param enc The JWE encryption method. Must not be {@code null}.
303         *
304         * @return {@code true} if the JWE algorithm is supported, else
305         *         {@code false}.
306         */
307        public static boolean isSupported(final EncryptionMethod enc) {
308
309                for (Provider p: Security.getProviders()) {
310
311                        if (isSupported(enc, p)) {
312                                return true;
313                        }
314                }
315
316                return false;
317        }
318
319
320        /**
321         * Checks if a JWE encryption method is supported by the specified
322         * JCA provider.
323         *
324         * @param enc      The JWE encryption method. Must not be {@code null}.
325         * @param provider The JCA provider. Must not be {@code null}.
326         *
327         * @return {@code true} if the JWE encryption method is supported, else
328         *         {@code false}.
329         */
330        public static boolean isSupported(final EncryptionMethod enc, final Provider provider) {
331
332                if (EncryptionMethod.Family.AES_CBC_HMAC_SHA.contains(enc)) {
333                        // Do direct test
334                        try {
335                                Cipher.getInstance("AES/CBC/PKCS5Padding", provider);
336                        } catch (NoSuchAlgorithmException e) {
337                                return false;
338                        } catch (NoSuchPaddingException e) {
339                                return false;
340                        }
341                        // Check hmac
342                        String hmac;
343                        if (enc.equals(EncryptionMethod.A128CBC_HS256)) {
344                                hmac = "HmacSHA256";
345                        } else if (enc.equals(EncryptionMethod.A192CBC_HS384)) {
346                                hmac = "HmacSHA384";
347                        } else {
348                                hmac = "HmacSHA512";
349                        }
350                        return provider.getService("KeyGenerator", hmac) != null;
351                }
352
353                if (EncryptionMethod.Family.AES_GCM.contains(enc)) {
354                        // Do direct test
355                        try {
356                                Cipher.getInstance("AES/GCM/NoPadding", provider);
357                        } catch (NoSuchAlgorithmException e) {
358                                return false;
359                        } catch (NoSuchPaddingException e) {
360                                return false;
361                        }
362                        return true;
363                }
364
365                return false;
366        }
367
368
369        /**
370         * Prevents public instantiation.
371         */
372        private JCASupport() {
373
374        }
375}