001package com.nimbusds.jose.jca;
002
003
004import java.security.NoSuchAlgorithmException;
005import java.security.Provider;
006import java.security.Security;
007
008import javax.crypto.Cipher;
009import javax.crypto.NoSuchPaddingException;
010
011import com.nimbusds.jose.EncryptionMethod;
012import com.nimbusds.jose.JWEAlgorithm;
013import com.nimbusds.jose.JWSAlgorithm;
014
015
016/**
017 * Java Cryptography Architecture (JCA) support helper.
018 */
019public final class JCASupport {
020
021
022        /**
023         * Checks if unlimited cryptographic strength is supported. If not
024         * download the appropriate jurisdiction policy files for your Java
025         * edition:
026         *
027         * <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>
028         *
029         * <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>
030         *
031         * @return {@code true} if unlimited cryptographic strength is
032         *         supported, {@code false} if not.
033         */
034        public static boolean isUnlimitedStrength() {
035
036                try {
037                        return Cipher.getMaxAllowedKeyLength("AES") >= 256;
038                } catch (NoSuchAlgorithmException e) {
039                        return false;
040                }
041        }
042
043
044        /**
045         * Checks if the specified JWS algorithm is supported by the default
046         * system JCA provider(s).
047         *
048         * @param alg The JWS algorithm. Must not be {@code null}.
049         *
050         * @return {@code true} if the JWS algorithm is supported, else
051         *         {@code false}.
052         */
053        public static boolean isSupported(final JWSAlgorithm alg) {
054
055                for (Provider p: Security.getProviders()) {
056
057                        if (isSupported(alg, p)) {
058                                return true;
059                        }
060                }
061
062                return false;
063        }
064
065
066        /**
067         * Checks if a JWS algorithm is supported by the the specified JCA
068         * provider.
069         *
070         * @param alg      The JWS algorithm. Must not be {@code null}.
071         * @param provider The JCA provider. Must not be {@code null}.
072         *
073         * @return {@code true} if the JWS algorithm is supported, else
074         *         {@code false}.
075         */
076        public static boolean isSupported(final JWSAlgorithm alg, final Provider provider) {
077
078                if (JWSAlgorithm.Family.HMAC_SHA.contains(alg)) {
079                        String jcaName;
080                        if (alg.equals(JWSAlgorithm.HS256)) {
081                                jcaName = "HMACSHA256";
082                        } else if (alg.equals(JWSAlgorithm.HS384)) {
083                                jcaName = "HMACSHA384";
084                        } else if (alg.equals(JWSAlgorithm.HS512)) {
085                                jcaName = "HMACSHA512";
086                        } else {
087                                return false;
088                        }
089                        return provider.getService("KeyGenerator", jcaName) != null;
090                }
091
092                if (JWSAlgorithm.Family.RSA.contains(alg)) {
093                        String jcaName;
094                        if (alg.equals(JWSAlgorithm.RS256)) {
095                                jcaName = "SHA256withRSA";
096                        } else if (alg.equals(JWSAlgorithm.RS384)) {
097                                jcaName = "SHA384withRSA";
098                        } else if (alg.equals(JWSAlgorithm.RS512)) {
099                                jcaName = "SHA512withRSA";
100                        } else if (alg.equals(JWSAlgorithm.PS256)) {
101                                jcaName = "SHA256withRSAandMGF1";
102                        } else if (alg.equals(JWSAlgorithm.PS384)) {
103                                jcaName = "SHA384withRSAandMGF1";
104                        } else if (alg.equals(JWSAlgorithm.PS512)) {
105                                jcaName = "SHA512withRSAandMGF1";
106                        } else {
107                                return false;
108                        }
109                        return provider.getService("Signature", jcaName) != null;
110                }
111
112                if (JWSAlgorithm.Family.EC.contains(alg)) {
113                        String jcaName;
114                        if (alg.equals(JWSAlgorithm.ES256)) {
115                                jcaName = "SHA256withECDSA";
116                        } else if (alg.equals(JWSAlgorithm.ES384)) {
117                                jcaName = "SHA384withECDSA";
118                        } else if (alg.equals(JWSAlgorithm.ES512)) {
119                                jcaName = "SHA512withECDSA";
120                        } else {
121                                return false;
122                        }
123                        return provider.getService("Signature", jcaName) != null;
124                }
125
126                return false;
127        }
128
129
130        /**
131         * Checks if the specified JWE algorithm is supported by the default
132         * system JCA provider(s).
133         *
134         * @param alg The JWE algorithm. Must not be {@code null}.
135         *
136         * @return {@code true} if the JWE algorithm is supported, else
137         *         {@code false}.
138         */
139        public static boolean isSupported(final JWEAlgorithm alg) {
140
141                for (Provider p: Security.getProviders()) {
142
143                        if (isSupported(alg, p)) {
144                                return true;
145                        }
146                }
147
148                return false;
149        }
150
151
152        /**
153         * Checks if a JWE algorithm is supported by the the specified JCA
154         * provider.
155         *
156         * @param alg      The JWE algorithm. Must not be {@code null}.
157         * @param provider The JCA provider. Must not be {@code null}.
158         *
159         * @return {@code true} if the JWE algorithm is supported, else
160         *         {@code false}.
161         */
162        public static boolean isSupported(final JWEAlgorithm alg, final Provider provider) {
163
164                String jcaName;
165
166                if (JWEAlgorithm.Family.RSA.contains(alg)) {
167                        if (alg.equals(JWEAlgorithm.RSA1_5)) {
168                                jcaName = "RSA/ECB/PKCS1Padding";
169                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP)) {
170                                jcaName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
171                        } else if (alg.equals(JWEAlgorithm.RSA_OAEP_256)) {
172                                jcaName = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
173                        } else {
174                                return false;
175                        }
176
177                        // Do direct test
178                        try {
179                                Cipher.getInstance(jcaName, provider);
180                        } catch (NoSuchAlgorithmException e) {
181                                return false;
182                        } catch (NoSuchPaddingException e) {
183                                return false;
184                        }
185                        return true;
186                }
187
188                if (JWEAlgorithm.Family.AES_KW.contains(alg)) {
189                        return provider.getService("Cipher", "AESWrap") != null;
190                }
191
192                if (JWEAlgorithm.Family.ECDH_ES.contains(alg)) {
193                        return provider.getService("KeyAgreement", "ECDH") != null;
194                }
195
196                if (JWEAlgorithm.Family.AES_GCM_KW.contains(alg)) {
197                        // Do direct test
198                        try {
199                                Cipher.getInstance("AES/GCM/NoPadding", provider);
200                        } catch (NoSuchAlgorithmException e) {
201                                return false;
202                        } catch (NoSuchPaddingException e) {
203                                return false;
204                        }
205                        return true;
206                }
207
208                if (JWEAlgorithm.Family.PBES2.contains(alg)) {
209                        String hmac;
210                        if (alg.equals(JWEAlgorithm.PBES2_HS256_A128KW)) {
211                                hmac = "HmacSHA256";
212                        } else if (alg.equals(JWEAlgorithm.PBES2_HS384_A192KW)) {
213                                hmac = "HmacSHA384";
214                        } else {
215                                hmac = "HmacSHA512";
216                        }
217                        return provider.getService("KeyGenerator", hmac) != null;
218                }
219
220                if (JWEAlgorithm.DIR.equals(alg)) {
221                        return true; // Always supported
222                }
223
224                return false;
225        }
226
227
228        /**
229         * Checks if the specified JWE encryption method is supported by the
230         * default system JCA provider(s).
231         *
232         * @param enc The JWE encryption method. Must not be {@code null}.
233         *
234         * @return {@code true} if the JWE algorithm is supported, else
235         *         {@code false}.
236         */
237        public static boolean isSupported(final EncryptionMethod enc) {
238
239                for (Provider p: Security.getProviders()) {
240
241                        if (isSupported(enc, p)) {
242                                return true;
243                        }
244                }
245
246                return false;
247        }
248
249
250        /**
251         * Checks if a JWE encryption method is supported by the specified
252         * JCA provider.
253         *
254         * @param enc      The JWE encryption method. Must not be {@code null}.
255         * @param provider The JCA provider. Must not be {@code null}.
256         *
257         * @return {@code true} if the JWE encryption method is supported, else
258         *         {@code false}.
259         */
260        public static boolean isSupported(final EncryptionMethod enc, final Provider provider) {
261
262                if (EncryptionMethod.Family.AES_CBC_HMAC_SHA.contains(enc)) {
263                        // Do direct test
264                        try {
265                                Cipher.getInstance("AES/CBC/PKCS5Padding", provider);
266                        } catch (NoSuchAlgorithmException e) {
267                                return false;
268                        } catch (NoSuchPaddingException e) {
269                                return false;
270                        }
271                        // Check hmac
272                        String hmac;
273                        if (enc.equals(EncryptionMethod.A128CBC_HS256)) {
274                                hmac = "HmacSHA256";
275                        } else if (enc.equals(EncryptionMethod.A192CBC_HS384)) {
276                                hmac = "HmacSHA384";
277                        } else {
278                                hmac = "HmacSHA512";
279                        }
280                        return provider.getService("KeyGenerator", hmac) != null;
281                }
282
283                if (EncryptionMethod.Family.AES_GCM.contains(enc)) {
284                        // Do direct test
285                        try {
286                                Cipher.getInstance("AES/GCM/NoPadding", provider);
287                        } catch (NoSuchAlgorithmException e) {
288                                return false;
289                        } catch (NoSuchPaddingException e) {
290                                return false;
291                        }
292                        return true;
293                }
294
295                return false;
296        }
297
298
299        /**
300         * Prevents public instantiation.
301         */
302        private JCASupport() {
303
304        }
305}