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 if (alg.equals(JWEAlgorithm.RSA_OAEP_512)) { 250 jcaName = "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"; 251 } else { 252 return false; 253 } 254 255 // Do direct test 256 try { 257 Cipher.getInstance(jcaName, provider); 258 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 259 return false; 260 } 261 return true; 262 } 263 264 if (JWEAlgorithm.Family.AES_KW.contains(alg)) { 265 return provider.getService("Cipher", "AESWrap") != null; 266 } 267 268 if (JWEAlgorithm.Family.ECDH_ES.contains(alg)) { 269 return provider.getService("KeyAgreement", "ECDH") != null; 270 } 271 272 if (JWEAlgorithm.Family.AES_GCM_KW.contains(alg)) { 273 // Do direct test 274 try { 275 Cipher.getInstance("AES/GCM/NoPadding", provider); 276 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 277 return false; 278 } 279 return true; 280 } 281 282 if (JWEAlgorithm.Family.PBES2.contains(alg)) { 283 String hmac; 284 if (alg.equals(JWEAlgorithm.PBES2_HS256_A128KW)) { 285 hmac = "HmacSHA256"; 286 } else if (alg.equals(JWEAlgorithm.PBES2_HS384_A192KW)) { 287 hmac = "HmacSHA384"; 288 } else { 289 hmac = "HmacSHA512"; 290 } 291 return provider.getService("KeyGenerator", hmac) != null; 292 } 293 294 if (JWEAlgorithm.DIR.equals(alg)) { 295 return true; // Always supported 296 } 297 298 return false; 299 } 300 301 302 /** 303 * Checks if the specified JWE encryption method is supported by the 304 * default system JCA provider(s). 305 * 306 * @param enc The JWE encryption method. Must not be {@code null}. 307 * 308 * @return {@code true} if the JWE algorithm is supported, else 309 * {@code false}. 310 */ 311 public static boolean isSupported(final EncryptionMethod enc) { 312 313 for (Provider p: Security.getProviders()) { 314 315 if (isSupported(enc, p)) { 316 return true; 317 } 318 } 319 320 return false; 321 } 322 323 324 /** 325 * Checks if a JWE encryption method is supported by the specified 326 * JCA provider. 327 * 328 * @param enc The JWE encryption method. Must not be {@code null}. 329 * @param provider The JCA provider. Must not be {@code null}. 330 * 331 * @return {@code true} if the JWE encryption method is supported, else 332 * {@code false}. 333 */ 334 public static boolean isSupported(final EncryptionMethod enc, final Provider provider) { 335 336 if (EncryptionMethod.Family.AES_CBC_HMAC_SHA.contains(enc)) { 337 // Do direct test 338 try { 339 Cipher.getInstance("AES/CBC/PKCS5Padding", provider); 340 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 341 return false; 342 } 343 // Check hmac 344 String hmac; 345 if (enc.equals(EncryptionMethod.A128CBC_HS256)) { 346 hmac = "HmacSHA256"; 347 } else if (enc.equals(EncryptionMethod.A192CBC_HS384)) { 348 hmac = "HmacSHA384"; 349 } else { 350 hmac = "HmacSHA512"; 351 } 352 return provider.getService("KeyGenerator", hmac) != null; 353 } 354 355 if (EncryptionMethod.Family.AES_GCM.contains(enc)) { 356 // Do direct test 357 try { 358 Cipher.getInstance("AES/GCM/NoPadding", provider); 359 } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 360 return false; 361 } 362 return true; 363 } 364 365 return false; 366 } 367 368 369 /** 370 * Prevents public instantiation. 371 */ 372 private JCASupport() { 373 374 } 375}