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