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