2424import java .security .PrivateKey ;
2525import java .security .PublicKey ;
2626import java .security .SecureRandom ;
27+ import java .security .spec .AlgorithmParameterSpec ;
28+ import java .security .spec .MGF1ParameterSpec ;
2729import java .util .ArrayList ;
2830import java .util .Arrays ;
2931import java .util .Collection ;
3032import java .util .List ;
3133import java .util .Map ;
3234import java .util .logging .Logger ;
35+ import java .util .regex .Matcher ;
3336import java .util .regex .Pattern ;
3437
3538import javax .crypto .Cipher ;
3639import javax .crypto .SecretKey ;
3740import javax .crypto .spec .GCMParameterSpec ;
41+ import javax .crypto .spec .OAEPParameterSpec ;
42+ import javax .crypto .spec .PSource ;
3843import javax .crypto .spec .SecretKeySpec ;
3944
4045import com .amazonaws .encryptionsdk .CryptoAlgorithm ;
@@ -237,17 +242,58 @@ public WrappingData(final Cipher cipher, final byte[] extraInfo) {
237242 }
238243
239244 private static class Rsa extends JceMasterKey {
245+ // MGF1 with SHA-224 isn't really supported, but we include it in the regex because we need it
246+ // for proper handling of the algorithm.
240247 private static final Pattern SUPPORTED_TRANSFORMATIONS =
241- Pattern .compile ("RSA/ECB/(?:PKCS1Padding|OAEPWithSHA -(?:1|256|384|512)AndMGF1Padding)" ,
248+ Pattern .compile ("RSA/ECB/(?:PKCS1Padding|OAEPWith(SHA -(?:1|224| 256|384|512) )AndMGF1Padding)" ,
242249 Pattern .CASE_INSENSITIVE );
250+ private final AlgorithmParameterSpec parameterSpec_ ;
243251 private final String transformation_ ;
244252
245253 private Rsa (PublicKey wrappingKey , PrivateKey unwrappingKey , String providerName , String keyId ,
246254 String transformation ) {
247255 super (wrappingKey , unwrappingKey , providerName , keyId );
248- transformation_ = transformation ;
249- if (!SUPPORTED_TRANSFORMATIONS .matcher (transformation_ ).matches ()) {
250- LOGGER .warning (transformation_ + " is not officially supported by the JceMasterKey" );
256+
257+ final Matcher matcher = SUPPORTED_TRANSFORMATIONS .matcher (transformation );
258+ if (matcher .matches ()) {
259+ final String hashUnknownCase = matcher .group (1 );
260+ if (hashUnknownCase != null ) {
261+ // OAEP mode a.k.a PKCS #1v2
262+ final String hash = hashUnknownCase .toUpperCase ();
263+ transformation_ = "RSA/ECB/OAEPPadding" ;
264+
265+ final MGF1ParameterSpec mgf1Spec ;
266+ switch (hash ) {
267+ case "SHA-1" :
268+ mgf1Spec = MGF1ParameterSpec .SHA1 ;
269+ break ;
270+ case "SHA-224" :
271+ LOGGER .warning (transformation + " is not officially supported by the JceMasterKey" );
272+ mgf1Spec = MGF1ParameterSpec .SHA224 ;
273+ break ;
274+ case "SHA-256" :
275+ mgf1Spec = MGF1ParameterSpec .SHA256 ;
276+ break ;
277+ case "SHA-384" :
278+ mgf1Spec = MGF1ParameterSpec .SHA384 ;
279+ break ;
280+ case "SHA-512" :
281+ mgf1Spec = MGF1ParameterSpec .SHA512 ;
282+ break ;
283+ default :
284+ throw new IllegalArgumentException ("Unsupported algorithm: " + transformation );
285+ }
286+ parameterSpec_ = new OAEPParameterSpec (hash , "MGF1" , mgf1Spec , PSource .PSpecified .DEFAULT );
287+ } else {
288+ // PKCS #1 v1.x
289+ transformation_ = transformation ;
290+ parameterSpec_ = null ;
291+ }
292+ } else {
293+ LOGGER .warning (transformation + " is not officially supported by the JceMasterKey" );
294+ // Unsupported transformation, just use exactly what we are given
295+ transformation_ = transformation ;
296+ parameterSpec_ = null ;
251297 }
252298 }
253299
@@ -256,8 +302,8 @@ protected WrappingData buildWrappingCipher(Key key, Map<String, String> encrypti
256302 throws GeneralSecurityException {
257303 // We require BouncyCastle to avoid some bugs in the default Java implementation
258304 // of OAEP.
259- final Cipher cipher = Cipher .getInstance (transformation_ , "BC" );
260- cipher .init (Cipher .ENCRYPT_MODE , key );
305+ final Cipher cipher = Cipher .getInstance (transformation_ );
306+ cipher .init (Cipher .ENCRYPT_MODE , key , parameterSpec_ );
261307 return new WrappingData (cipher , EMPTY_ARRAY );
262308 }
263309
@@ -269,8 +315,8 @@ protected Cipher buildUnwrappingCipher(Key key, byte[] extraInfo, int offset,
269315 }
270316 // We require BouncyCastle to avoid some bugs in the default Java implementation
271317 // of OAEP.
272- final Cipher cipher = Cipher .getInstance (transformation_ , "BC" );
273- cipher .init (Cipher .DECRYPT_MODE , key );
318+ final Cipher cipher = Cipher .getInstance (transformation_ );
319+ cipher .init (Cipher .DECRYPT_MODE , key , parameterSpec_ );
274320 return cipher ;
275321 }
276322 }
0 commit comments