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