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