'What "SecretKeyFactory not available" does mean?

What's wrong with this?

for (Object obj : java.security.Security.getAlgorithms("Cipher")) {
  System.out.println(obj);
}
javax.crypto.SecretKeyFactory.getInstance("AES");

This is the output (JDK 1.6 on Mac OS 10.6):

BLOWFISH
ARCFOUR
PBEWITHMD5ANDDES
RC2
RSA
PBEWITHMD5ANDTRIPLEDES
PBEWITHSHA1ANDDESEDE
DESEDE
AESWRAP
AES
DES
DESEDEWRAP
PBEWITHSHA1ANDRC2_40

java.security.NoSuchAlgorithmException: AES SecretKeyFactory not available
 at javax.crypto.SecretKeyFactory.<init>(DashoA13*..)
 at javax.crypto.SecretKeyFactory.getInstance(DashoA13*..)
 ...


Solution 1:[1]

This is a verified java bug. See https://bugs.openjdk.java.net/browse/JDK-7022467

EDIT: Different java versions support different algorithms, you can also extend it with custom providers and so on. Oracle has a list for java 6 here http://docs.oracle.com/javase/6/docs/technotes/guides/security/SunProviders.html . For KeyFactory this is DSA.

Solution 2:[2]

You don't really need to use SecretKeyFactory. You can create an AES key with the following;

byte[] keyData = ........ 
SecretKeySpec key = new SecretKeySpec(keyData, "AES");

If you want to do password based encryption (PBE) then simply choose a secure hashing algorithm that gives you a hash the same size as the required key. For example, if you want a 256 bit key for AES, here is a method to build the key;

private Key buildKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
  MessageDigest digester = MessageDigest.getInstance("SHA-256");
  digester.update(password.getBytes("UTF-8"));
  byte[] key = digester.digest();
  SecretKeySpec spec = new SecretKeySpec(key, "AES");
  return spec;
}

Edit:
I would recommend against using MD5 and DES unless this is a play project, both have weaknesses and are considered obsolete.

Solution 3:[3]

Not all versions of Java provide a SecretKeyFactory for "AES" in their default providers.

If you want to generate a new key, choose the desired number of bits (128, 192, or 256) from a SecureRandom instance, and use that random number to initialize a SecretKeySpec instance.

If you are using password-based encryption, create a SecretKeyFactory for the "PBKDF2WithHmacSHA1" algorithm, and use it to initialize a SecretKeySpec instance as illustrated here.

Solution 4:[4]

If you try to print all your providers, you probably missed the one that you need. Try to call this method and see what it prints out.

for (Object obj : java.security.Security.getProviders()) {
    System.out.println(obj);
}

In case for example you need a specific provider (like the Bouncy Castle one), add the dependency to your App and add it on Security Providers as follows:

java.security.Security.addProvider(new BouncyCastleProvider());

and then see again your provider list.

Give a try with all of your algorithm and see which one are supported. An example can be done with jasypt encryption

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.registry.AlgorithmRegistry;

with the following code:

Set<String> supported = new TreeSet<>();
Set<String> unsupported = new TreeSet<>();
for (Object oAlgorithm : AlgorithmRegistry.getAllPBEAlgorithms()) {
    String algorithm = (String) oAlgorithm;
    try {
        SimpleStringPBEConfig pbeConfig = new SimpleStringPBEConfig();
        pbeConfig.setAlgorithm(algorithm);
        pbeConfig.setPassword("changeme");
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setConfig(pbeConfig);
        String encrypted = encryptor.encrypt("foo");
        String decrypted = encryptor.decrypt(encrypted);
        supported.add(algorithm);
    } catch (EncryptionOperationNotPossibleException e) {
        unsupported.add(algorithm);
    }
}
System.out.println("Supported");
supported.forEach((String alg) -> System.out.println("   " + alg));
System.out.println("Unsupported");
unsupported.forEach((String alg) -> System.out.println("   " + alg));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 VGR
Solution 2 Qwerky
Solution 3 Community
Solution 4 Aerox