0

I have a unit test which passes in Java 8+ and fails on Java 7 because GCM mode doesn't seem to be supported in Java 7 and earlier.

I know that I can attempt to Create a cipher with the transform e.g. AES/GCM/PKCS5Padding and catch NoSuchAlgorithmException, but that exception might be thrown just for that particular transform and not just because GCM mode itself isn't supported (within any transform).

I can also just check for the JVM version, but that wouldn't be a valid check for an environment where a 3rd-party crypto library is in use (e.g. BouncyCastle) or a JVM with built-in support from another vendor which happens to include support for GCM.

I'd prefer to skip the test only if GCM in general is not supported, and not just if the particular (and complete) transform I choose in my code is not supported.

Is it possible to detect supported cipher block modes from Java? Or is it only possible to try a particular transform and see if it works?

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
  • That's a weird requirement, and thus I'm intrigued. What if you found out that GCM was supported, by only by a weak encryption algorithm? – President James K. Polk Jan 07 '19 at 00:37
  • @JamesKPolk AFAIK GCM is only supported by 128 bit block ciphers and NIST only defines it for AES. The chance of finding something that is not `"AES/GCM/Nopadding"` is therefore minimal, and if you find it it would still use a 128 bit block cipher, which is very likely secure. But yeah, I'm intrigued as well. I don't know of any protocols that specify a mode and leave the actual cipher / padding mode undefined for the mode of operation. – Maarten Bodewes Jan 07 '19 at 09:26
  • Java 8 supports `PKCS5Padding` with `AES/GCM` among other paddings as well. Java 7 supports no GCM at all. I was just trying to be as careful as possible to only rule-out a test if it's really impossible to test it. – Christopher Schultz Jan 07 '19 at 17:20

1 Answers1

2

Yes, you can traverse through the providers and services and look for a service that is a cipher and contains GCM in the name, e.g.

Provider[] provs = Security.getProviders();
for (Provider prov : provs) {
    Set<Service> services = prov.getServices();
    for (Service service : services) {
        if (!service.getType().matches("(?i)Cipher")) {
            break;
        }
        String algo = service.getAlgorithm();
        if (algo.matches("(?i).*/GCM/.*")) {
            System.out.println(service);
        }
    }
}

Beware that you may want to check for unlimited crypto for older Java runtimes, e.g. using my answer here.

You may want to consider that GCM is only valid for 128 bit ciphers, and that it is unlikely that you'll find implementations that do not use AES. Furthermore, there aren't any parameters other than "NoPadding" that make sense for GCM mode (in the algorithm string anyway, I'm not talking about GCCMParameterSpec of course).

Beware that later providers may not return "AES/GCM/NoPadding" but return "AES_128/GCM/NoPadding", "AES_192/GCM/NoPadding" and "AES_256/GCM/NoPadding" instead. This also influences, the Provider#getService(type, algorithm) call, rendering it useless in case you want to check for "AES/GCM/NoPadding", i.e. AES with any valid key size.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I'm already checking for key strength and skipping the tests for AES with keys larger than 128 bits. Oddly enough, I knew I could enumerate the security providers and their algorithms but it never occurred to me to just perform string comparisons on them. Thanks! – Christopher Schultz Jan 07 '19 at 17:22
  • The algorithm match should probably be `(?i).*/GCM/.*` to check just for the block cipher mode.... just in case someone invents some algorithm that happens to include "GCM" in its cipher name. – Christopher Schultz Jan 07 '19 at 17:24
  • @ChristopherSchultz That's actually a change I made in the code locally too yes. I'll change it here too. Note that there is also `Provider#getService(String type, String algorithm)`, but it required me to use `"AES_128/GCM/NoPadding"` instead of `"AES/GCM/NoPadding"` in Java 11. I'm planning on filing a regression bug for that, not useful, not symmetric with the `getInstance` methods this way. – Maarten Bodewes Jan 07 '19 at 17:30