What "SecretKeyFactory not available" does mean?
Asked Answered
C

5

9

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*..)
 ...
Crites answered 6/12, 2011 at 8:25 Comment(7)
Actually I'm getting this exception when trying to use jasypt library. I posted a problem to their forum already: forum.jasypt.org/…Crites
Have you installed the policy files as stated on this page? jasypt.org/dependencies.htmlMealtime
No, I didn't. Do you know how to add them to Maven project?Crites
You need to add them to the jdk itselfMealtime
Actually when reading: docs.oracle.com/javase/6/docs/technotes/guides/security/… it sais that SunJCEProvider is optional. You need PBEWithMD5AndDES and this is the provider responsible.Mealtime
PBEWithMD5AndDES SecretKeyFactory not availableCrites
For Java 1.8, I was getting the same error until I found a workaround mentioned in JDK-8185292. I added the following property in code and that fixed the problem: System.setProperty("jdk.crypto.KeyAgreement.legacyKDF","true");Pomeranian
M
7

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.

Mealtime answered 6/12, 2011 at 8:28 Comment(3)
I'm getting the same exception for almost all algorithms from the list (only DES works).Crites
That bug is now at bugs.openjdk.java.net/browse/JDK-7022467. The original is viewable at web.archive.org/web/20130430082811/http://bugs.sun.com/….Missioner
@Missioner feel free to edit the answer!Mealtime
O
4

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.

Opheliaophelie answered 6/12, 2011 at 9:54 Comment(3)
Simply hashing the password is not a secure way to derive a password. Use a recognized key derivation algorithm (also provided by JCA) to convert text to a secret key.Horary
@Horary But the final 'evaluation' of the relevant issue (linked in the answer below) suggests to me that the above solution is the recommended solution for AES. How could we improve the above solution, without going back to SecretKeyFactory which is not going to support AES.Wortman
@tom The final evaluation doesn't suggest using a single round of hashing to perform password-based encryption. Please see my answer for safely following the evaluation's guidance.Horary
H
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.

Horary answered 16/3, 2013 at 20:45 Comment(4)
Be careful about that combination - big android bug fixed in 4.4 means that pre & post 4.4 produce different results. android-developers.blogspot.ca/2013/12/…Wortman
Don't be careful with PBKDF2WithHmacSHA1; it's the algorithm you should use, and decrypts messages encrypted with SunJCE. If you have to work with data that was encrypted using the old, buggy version of Android, then you'll have to be aware of the non-standard 8bit algorithm. If you are using SunJCE to decrypt messages encrypted with buggy Android implementations, you'll need to set the upper 8 bits of each char in the password to zero yourself, as it doesn't provide an algorithm with this behavior.Horary
I didn't say don't use it - I just said to take care, and since there is an issue with Android (and BouncyCastle too, I believe) I don't think that is unwarranted.Wortman
That makes sense. I'm stressing the point because the leading answer has a horrible security flaw, simply using a hash function to derive a key.Horary
B
2

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));
Brucebrucellosis answered 12/1, 2022 at 16:26 Comment(2)
I had an similar issue with Bouncy Castle providers. Adding Provider to java security resolved my issue. Thanks java.security.Security.addProvider(new BouncyCastleProvider());Classicize
how you added this to java security, please tell detailsHughes
H
0

For Java 17 JDK-17.0.7, we were facing the similar error Caused by: java.security.NoSuchAlgorithmException: PBKDF2with8BIT SecretKeyFactory not available. Despite setting the java security dynamically in code with "Security.addProvider(new BouncyCastleProvider());" . we continued to face the error.

Solution that worked was

Changed the version of JDK, from jdk-17.0.7 to openJdk-17.0.7+7 This solved the issue, without facing any exception.

Hughes answered 18/4, 2024 at 16:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.