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;
024
025import javax.crypto.Cipher;
026import javax.crypto.NoSuchPaddingException;
027
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 JWS algorithm is supported by the default
063         * system JCA provider(s).
064         *
065         * @param alg The JWS algorithm. Must not be {@code null}.
066         *
067         * @return {@code true} if the JWS algorithm is supported, else
068         *         {@code false}.
069         */
070        public static boolean isSupported(final JWSAlgorithm alg) {
071
072                for (Provider p: Security.getProviders()) {
073
074                        if (isSupported(alg, p)) {
075                                return true;
076                        }
077                }
078
079                return false;
080        }
081
082
083        /**
084         * Checks if a JWS algorithm is supported by the the specified JCA
085         * provider.
086         *
087         * @param alg      The JWS algorithm. Must not be {@code null}.
088         * @param provider The JCA provider. Must not be {@code null}.
089         *
090         * @return {@code true} if the JWS algorithm is supported, else
091         *         {@code false}.
092         */
093        public static boolean isSupported(final JWSAlgorithm alg, final Provider provider) {
094
095                if (JWSAlgorithm.Family.HMAC_SHA.contains(alg)) {
096                        String jcaName;
097                        if (alg.equals(JWSAlgorithm.HS256)) {
098                                jcaName = "HMACSHA256";
099                        } else if (alg.equals(JWSAlgorithm.HS384)) {
100                                jcaName = "HMACSHA384";
101                        } else if (alg.equals(JWSAlgorithm.HS512)) {
102                                jcaName = "HMACSHA512";
103                        } else {
104                                return false;
105                        }
106                        return provider.getService("KeyGenerator", jcaName) != null;
107                }
108
109                if (JWSAlgorithm.Family.RSA.contains(alg)) {
110                        String jcaName;
111                        if (alg.equals(JWSAlgorithm.RS256)) {
112                                jcaName = "SHA256withRSA";
113                        } else if (alg.equals(JWSAlgorithm.RS384)) {
114                                jcaName = "SHA384withRSA";
115                        } else if (alg.equals(JWSAlgorithm.RS512)) {
116                                jcaName = "SHA512withRSA";
117                        } else if (alg.equals(JWSAlgorithm.PS256)) {
118                                jcaName = "SHA256withRSAandMGF1";
119                        } else if (alg.equals(JWSAlgorithm.PS384)) {
120                                jcaName = "SHA384withRSAandMGF1";
121                        } else if (alg.equals(JWSAlgorithm.PS512)) {
122                                jcaName = "SHA512withRSAandMGF1";
123                        } else {
124                                return false;
125                        }
126                        return provider.getService("Signature", jcaName) != null;
127                }
128
129                if (JWSAlgorithm.Family.EC.contains(alg)) {
130                        String jcaName;
131                        if (alg.equals(JWSAlgorithm.ES256)) {
132                                jcaName = "SHA256withECDSA";
133                        } else if (alg.equals(JWSAlgorithm.ES384)) {
134                                jcaName = "SHA384withECDSA";
135                        } else if (alg.equals(JWSAlgorithm.ES512)) {
136                                jcaName = "SHA512withECDSA";
137                        } else {
138                                return false;
139                        }
140                        return provider.getService("Signature", jcaName) != null;
141                }
142
143                return false;
144        }
145
146
147        /**
148         * Checks if the specified JWE algorithm is supported by the default
149         * system JCA provider(s).
150         *
151         * @param alg The JWE algorithm. Must not be {@code null}.
152         *
153         * @return {@code true} if the JWE algorithm is supported, else
154         *         {@code false}.
155         */
156        public static boolean isSupported(final JWEAlgorithm alg) {
157
158                for (Provider p: Security.getProviders()) {
159
160                        if (isSupported(alg, p)) {
161                                return true;
162                        }
163                }
164
165                return false;
166        }
167
168
169        /**
170         * Checks if a JWE algorithm is supported by the the specified JCA
171         * provider.
172         *
173         * @param alg      The JWE algorithm. Must not be {@code null}.
174         * @param provider The JCA provider. Must not be {@code null}.
175         *
176         * @return {@code true} if the JWE algorithm is supported, else
177         *         {@code false}.
178         */
179        public static boolean isSupported(final JWEAlgorithm alg, final Provider provider) {
180
181                String jcaName;
182
183                if (JWEAlgorithm.Family.RSA.contains(alg)) {
184                        if (alg.equals(JWEAlgorithm.RSA1_5)) {
185                                jcaName = "RSA/ECB/PKCS1Padding";
186                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
187                                jcaName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
188                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
189                                jcaName = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
190                        } else {
191                                return false;
192                        }
193
194                        // Do direct test
195                        try {
196                                Cipher.getInstance(jcaName, provider);
197                        } catch (NoSuchAlgorithmException e) {
198                                return false;
199                        } catch (NoSuchPaddingException e) {
200                                return false;
201                        }
202                        return true;
203                }
204
205                if (JWEAlgorithm.Family.AES_KW.contains(alg)) {
206                        return provider.getService("Cipher", "AESWrap") != null;
207                }
208
209                if (JWEAlgorithm.Family.ECDH_ES.contains(alg)) {
210                        return provider.getService("KeyAgreement", "ECDH") != null;
211                }
212
213                if (JWEAlgorithm.Family.AES_GCM_KW.contains(alg)) {
214                        // Do direct test
215                        try {
216                                Cipher.getInstance("AES/GCM/NoPadding", provider);
217                        } catch (NoSuchAlgorithmException e) {
218                                return false;
219                        } catch (NoSuchPaddingException e) {
220                                return false;
221                        }
222                        return true;
223                }
224
225                if (JWEAlgorithm.Family.PBES2.contains(alg)) {
226                        String hmac;
227                        if (alg.equals(JWEAlgorithm.PBES2_HS256_A128KW)) {
228                                hmac = "HmacSHA256";
229                        } else if (alg.equals(JWEAlgorithm.PBES2_HS384_A192KW)) {
230                                hmac = "HmacSHA384";
231                        } else {
232                                hmac = "HmacSHA512";
233                        }
234                        return provider.getService("KeyGenerator", hmac) != null;
235                }
236
237                if (JWEAlgorithm.DIR.equals(alg)) {
238                        return true; // Always supported
239                }
240
241                return false;
242        }
243
244
245        /**
246         * Checks if the specified JWE encryption method is supported by the
247         * default system JCA provider(s).
248         *
249         * @param enc The JWE encryption method. Must not be {@code null}.
250         *
251         * @return {@code true} if the JWE algorithm is supported, else
252         *         {@code false}.
253         */
254        public static boolean isSupported(final EncryptionMethod enc) {
255
256                for (Provider p: Security.getProviders()) {
257
258                        if (isSupported(enc, p)) {
259                                return true;
260                        }
261                }
262
263                return false;
264        }
265
266
267        /**
268         * Checks if a JWE encryption method is supported by the specified
269         * JCA provider.
270         *
271         * @param enc      The JWE encryption method. Must not be {@code null}.
272         * @param provider The JCA provider. Must not be {@code null}.
273         *
274         * @return {@code true} if the JWE encryption method is supported, else
275         *         {@code false}.
276         */
277        public static boolean isSupported(final EncryptionMethod enc, final Provider provider) {
278
279                if (EncryptionMethod.Family.AES_CBC_HMAC_SHA.contains(enc)) {
280                        // Do direct test
281                        try {
282                                Cipher.getInstance("AES/CBC/PKCS5Padding", provider);
283                        } catch (NoSuchAlgorithmException e) {
284                                return false;
285                        } catch (NoSuchPaddingException e) {
286                                return false;
287                        }
288                        // Check hmac
289                        String hmac;
290                        if (enc.equals(EncryptionMethod.A128CBC_HS256)) {
291                                hmac = "HmacSHA256";
292                        } else if (enc.equals(EncryptionMethod.A192CBC_HS384)) {
293                                hmac = "HmacSHA384";
294                        } else {
295                                hmac = "HmacSHA512";
296                        }
297                        return provider.getService("KeyGenerator", hmac) != null;
298                }
299
300                if (EncryptionMethod.Family.AES_GCM.contains(enc)) {
301                        // Do direct test
302                        try {
303                                Cipher.getInstance("AES/GCM/NoPadding", provider);
304                        } catch (NoSuchAlgorithmException e) {
305                                return false;
306                        } catch (NoSuchPaddingException e) {
307                                return false;
308                        }
309                        return true;
310                }
311
312                return false;
313        }
314
315
316        /**
317         * Prevents public instantiation.
318         */
319        private JCASupport() {
320
321        }
322}