JDK-8218723 : Use SunJCE Mac in SecretKeyFactory PBKDF2 implementation
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.crypto
  • Affected Version: 9,10,11,12,13
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2019-02-08
  • Updated: 2019-10-04
  • Resolved: 2019-03-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13
11.0.5Fixed 13 b13Fixed
Related Reports
CSR :  
Relates :  
Sub Tasks
JDK-8220791 :  
Description
ADDITIONAL SYSTEM INFORMATION :
generic / generic / java version "11.0.2" 2019-01-15 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.2+9-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.2+9-LTS, mixed mode)

A DESCRIPTION OF THE PROBLEM :
A call to SecretKeyFactory getInstance( algo_, provider_ ), ignores the provider argument (in this example "SunJCE") and always uses the first provider in the provider list when calculating HMAC. 

When adding third party crypto providers, such as bc-fips-1.0.1.jar as first provider and invoking the code  

PBEKeySpec spec = new PBEKeySpec( ""Test#1234".toCharArray(), "[B@2054babb".getBytes(), 10000, 16 * 8 );
SecretKeyFactory skf = SecretKeyFactory.getInstance( "PBKDF2WithHmacSHA1", "SunJCE" );
skf.generateSecret( spec ).getEncoded();

leads to:

org.bouncycastle.crypto.IllegalKeyException: Key size for HMAC must be at least 112 bits in approved mode: SHA-1/HMAC
	at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source)
	at org.bouncycastle.jcajce.provider.BaseHMac.engineInit(Unknown Source)
	at java.base/javax.crypto.Mac.init(Mac.java:433)
	at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.deriveKey(PBKDF2KeyImpl.java:182)
	at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.<init>(PBKDF2KeyImpl.java:122)
	at java.base/com.sun.crypto.provider.PBKDF2Core.engineGenerateSecret(PBKDF2Core.java:69)
	at java.base/javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:338)

This worked earlier with Java 8 Update 192

REGRESSION : Last worked in version 8u192

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Add BC FIPS Provider at position 1 and set the FIPS approved mode to true. (Code attached) 
2. Generate Secret using "SunJCE" provider as shown below. (Code attached)

PBEKeySpec spec = new PBEKeySpec( ""Test#1234".toCharArray(), "[B@2054babb".getBytes(), 10000, 16 * 8 );
SecretKeyFactory skf = SecretKeyFactory.getInstance( "PBKDF2WithHmacSHA1", "SunJCE" );
skf.generateSecret( spec ).getEncoded();

Compile and run the SecretKeyFactoryUsingSunJCETest class with bc-fips-1.0.1.jar added to the classpath

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Java SecurityProvider list:

Provider at position = 1 has name = SUN
Provider at position = 2 has name = SunRsaSign
Provider at position = 3 has name = SunEC
Provider at position = 4 has name = SunJSSE
Provider at position = 5 has name = SunJCE
Provider at position = 6 has name = SunJGSS
Provider at position = 7 has name = SunSASL
Provider at position = 8 has name = XMLDSig
Provider at position = 9 has name = SunPCSC
Provider at position = 10 has name = SunMSCAPI

Added Provider to Position 1: BCFIPS

BouncyCastle FIPStatus: true
BouncyCastle FIPS Approved Mode: true

Java SecurityProvider list:

Provider at position = 1 has name = BCFIPS
Provider at position = 2 has name = SUN
Provider at position = 3 has name = SunRsaSign
Provider at position = 4 has name = SunEC
Provider at position = 5 has name = SunJSSE
Provider at position = 6 has name = SunJCE
Provider at position = 7 has name = SunJGSS
Provider at position = 8 has name = SunSASL
Provider at position = 9 has name = XMLDSig
Provider at position = 10 has name = SunPCSC
Provider at position = 11 has name = SunMSCAPI
Using JCE_PROVIDER for SecretKeyFactory ## = SunJCE
Secret Code: [B@4c1bdcc2

Process finished with exit code 0
ACTUAL -
Java SecurityProvider list:

Provider at position = 1 has name = SUN
Provider at position = 2 has name = SunRsaSign
Provider at position = 3 has name = SunEC
Provider at position = 4 has name = SunJSSE
Provider at position = 5 has name = SunJCE
Provider at position = 6 has name = SunJGSS
Provider at position = 7 has name = SunSASL
Provider at position = 8 has name = XMLDSig
Provider at position = 9 has name = SunPCSC
Provider at position = 10 has name = SunMSCAPI

Added Provider to Position 1: BCFIPS

BouncyCastle FIPStatus: true
BouncyCastle FIPS Approved Mode: true

Java SecurityProvider list:

Provider at position = 1 has name = BCFIPS
Provider at position = 2 has name = SUN
Provider at position = 3 has name = SunRsaSign
Provider at position = 4 has name = SunEC
Provider at position = 5 has name = SunJSSE
Provider at position = 6 has name = SunJCE
Provider at position = 7 has name = SunJGSS
Provider at position = 8 has name = SunSASL
Provider at position = 9 has name = XMLDSig
Provider at position = 10 has name = SunPCSC
Provider at position = 11 has name = SunMSCAPI
Using JCE_PROVIDER for SecretKeyFactory ## = SunJCE
org.bouncycastle.crypto.IllegalKeyException: Key size for HMAC must be at least 112 bits in approved mode: SHA-1/HMAC
	at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source)
	at org.bouncycastle.jcajce.provider.BaseHMac.engineInit(Unknown Source)
	at java.base/javax.crypto.Mac.init(Mac.java:433)
	at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.deriveKey(PBKDF2KeyImpl.java:182)
	at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.<init>(PBKDF2KeyImpl.java:122)
	at java.base/com.sun.crypto.provider.PBKDF2Core.engineGenerateSecret(PBKDF2Core.java:69)
	at java.base/javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:338)
	at PBKDF2HmacSHA1Test.pbkdf2Encryption(PBKDF2HmacSHA1Test.java:72)
	at PBKDF2HmacSHA1Test.generateSecret(PBKDF2HmacSHA1Test.java:55)
	at PBKDF2HmacSHA1Test.generateSecretCodeUsingBouncyCastle(PBKDF2HmacSHA1Test.java:40)
	at PBKDF2HmacSHA1Test.main(PBKDF2HmacSHA1Test.java:27)
Exception in thread "main" org.bouncycastle.crypto.IllegalKeyException: Key size for HMAC must be at least 112 bits in approved mode: SHA-1/HMAC
	at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source)
	at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source)
	at org.bouncycastle.jcajce.provider.BaseHMac.engineInit(Unknown Source)
	at java.base/javax.crypto.Mac.init(Mac.java:433)
	at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.deriveKey(PBKDF2KeyImpl.java:182)
	at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.<init>(PBKDF2KeyImpl.java:122)
	at java.base/com.sun.crypto.provider.PBKDF2Core.engineGenerateSecret(PBKDF2Core.java:69)
	at java.base/javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:338)
	at PBKDF2HmacSHA1Test.pbkdf2Encryption(PBKDF2HmacSHA1Test.java:72)
	at PBKDF2HmacSHA1Test.generateSecret(PBKDF2HmacSHA1Test.java:55)
	at PBKDF2HmacSHA1Test.generateSecretCodeUsingBouncyCastle(PBKDF2HmacSHA1Test.java:40)
	at PBKDF2HmacSHA1Test.main(PBKDF2HmacSHA1Test.java:27)

Process finished with exit code 1

---------- BEGIN SOURCE ----------
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;

public class SecretKeyFactoryUsingSunJCETest
{
  private static final String PBKDF2_SHA_ALGORITHM = "PBKDF2WithHmacSHA1";

  public static final String SUN_JCE_PROVIDER = "SunJCE";
  public static final String BOUNCYCASTLE_JCE_PROVIDER = "BCFIPS";

  public static final int HASH_BYTE_SIZE = 16;
  public static final int PBKDF2_ITERATIONS = 10000;

  public static final String PASSWORD = "Test#1234";
  public static final String SALT = "[B@2054babb";

  public static void main( String[] args_ ) throws Exception
  {
    byte[] secretCode = null;

    secretCode = generateSecretCode();

    System.out.println("Secret Code: " + secretCode);
  }

  private static byte[] generateSecretCode() throws InvalidKeySpecException, NoSuchAlgorithmException
  {
    byte[] secretCode = null;
    printProviders();
    addBouncyCastleProvider( true );     // Add BouncyCastle provider to the security providers list
    printProviders();
    secretCode = generateSecret( PASSWORD, SALT, SUN_JCE_PROVIDER );
    removeBouncyCastleProvider();
    return secretCode;
  }

  public static byte[] generateSecret( String data_, String salt_, String provider_ )
    throws InvalidKeySpecException, NoSuchAlgorithmException
  {
    // Hash the password
    byte[] hash = new byte[0];
    try
    {
      hash = pbkdf2Encryption( data_.toCharArray(), salt_.getBytes(), PBKDF2_ITERATIONS, HASH_BYTE_SIZE, provider_ );
    }
    catch( Exception e )
    {
      e.printStackTrace();
      throw e;
    }

    return hash;
  }

  private static byte[] pbkdf2Encryption( char[] password_, byte[] salt_, int iterations_, int bytes_,
                                          String provider_ )
    throws NoSuchAlgorithmException, InvalidKeySpecException, SecurityException
  {
    PBEKeySpec spec = new PBEKeySpec( password_, salt_, iterations_, bytes_ * 8 );
    SecretKeyFactory skf = getSecretKeyFactoryInstance( PBKDF2_SHA_ALGORITHM, provider_ );
    return skf.generateSecret( spec ).getEncoded();
  }

  public static SecretKeyFactory getSecretKeyFactoryInstance( String algorithm_, String provider_ )
    throws SecurityException
  {
    SecretKeyFactory secretKeyFactory = null;
    try
    {
      secretKeyFactory = SecretKeyFactory.getInstance( algorithm_, provider_ );
    }
    catch( NoSuchAlgorithmException | NoSuchProviderException e )
    {
      e.printStackTrace();
    }

    System.out.println( "Using JCE_PROVIDER for SecretKeyFactory ## = " + secretKeyFactory.getProvider().getName() );

    return secretKeyFactory;
  }

  public static void addBouncyCastleProvider( boolean fipsMode_ )
  {
    Provider bouncyCastleFipsProvider = new org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider();

    addProvider( BOUNCYCASTLE_JCE_PROVIDER, bouncyCastleFipsProvider );

    // Set FIPS Approved Mode ON - BouncyCastle
    org.bouncycastle.crypto.CryptoServicesRegistrar.setApprovedOnlyMode( fipsMode_ );

    System.out.println( "BouncyCastle FIPStatus: " +
                        org.bouncycastle.crypto.fips.FipsStatus.isReady() );
    System.out.println( "BouncyCastle FIPS Approved Mode: " +
                        org.bouncycastle.crypto.CryptoServicesRegistrar.isInApprovedOnlyMode() );
  }

  private static void addProvider( String providerName_, Provider provider_ )
  {
    try
    {
      int index = getProviderIndex( providerName_ );

      // If the provider is already present first remove it.
      if( (index != -1) )
      {
        Security.removeProvider( providerName_ );
      }
      Security.insertProviderAt( provider_, 1 );
    }
    catch( Exception e )
    {
      throw new SecurityException( "Could not add Security Provider " + providerName_, e );
    }

    System.out.println("\nAdded Provider to Position 1: " + providerName_ + "\n");
  }

  public static void removeBouncyCastleProvider()
    throws SecurityException
  {
    removeProvider( BOUNCYCASTLE_JCE_PROVIDER );
  }

  private static void removeProvider( String providerName_ )
  {
    try
    {
      Security.removeProvider( providerName_ );
    }
    catch( Exception e )
    {
      throw new SecurityException( "Could not add Crypto-J JCE Provider.", e );
    }
  }

  public static void printProviders()
  {
    System.out.println( "\nJava SecurityProvider list:\n" );
    Provider[] providers_ = Security.getProviders();
    int i = 1;
    for( Provider provider : providers_ )
    {
      System.out.println( "Provider at position = " + i + " has name = " + provider.getName() );
      i++;
    }
  }

  public static int getProviderIndex( String providerName_ )
  {
    int providerIndex = 1;
    if( providerName_ == null )
    {
      return -1;
    }
    Provider[] providers_ = Security.getProviders();
    for( Provider provider : providers_ )
    {
      if( providerName_.equalsIgnoreCase( provider.getName() ) )
      {
        return providerIndex;
      }
      providerIndex++;
    }
    return -1;
  }
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
Verified by running test javax/crypto/SecretKeyFactory/SecKeyFacSunJCEPrf.java with jdk13+27.
01-07-2019

Fix request (11u): Request backport of this item for parity with Oracle 11.0.5. Patch applies Cleanly and will be tested at SAP. Note that this bug has a CSR attached (JDK-8220531). So I manually created a backport bug (JDK-8226637) and linked it to the Oracle 11.0.5 CSR (JDK-8223278).
22-06-2019

This appears to be caused by JDK-6977937 (The SunJCE PBKDF2KeyImpl is requiring the MAC instance also be from SunJCE.). The new code calls Mac.getInstance w/o a provider argument, which is why it selects the highest provider. It might make more sense to use the provider passed to SecretKeyFactory, if it is specified.
11-02-2019

To reproduce the issue, run the attached test case : JDK 8u201- Pass JDK 9 GA - Fail JDK 11.0.1 - Fail JDK 12-ea + 30 - Fail JDK 13-ea + 6 - Fail Output on JDK 8u201 : >D:\jdk8u201\bin\java -cp .;D:\bc-fips-1.0.1.jar JI9059298 Java SecurityProvider list: Provider at position = 1 has name = SUN Provider at position = 2 has name = SunRsaSign Provider at position = 3 has name = SunEC Provider at position = 4 has name = SunJSSE Provider at position = 5 has name = SunJCE Provider at position = 6 has name = SunJGSS Provider at position = 7 has name = SunSASL Provider at position = 8 has name = XMLDSig Provider at position = 9 has name = SunPCSC Provider at position = 10 has name = SunMSCAPI Added Provider to Position 1: BCFIPS BouncyCastle FIPStatus: true BouncyCastle FIPS Approved Mode: true Java SecurityProvider list: Provider at position = 1 has name = BCFIPS Provider at position = 2 has name = SUN Provider at position = 3 has name = SunRsaSign Provider at position = 4 has name = SunEC Provider at position = 5 has name = SunJSSE Provider at position = 6 has name = SunJCE Provider at position = 7 has name = SunJGSS Provider at position = 8 has name = SunSASL Provider at position = 9 has name = XMLDSig Provider at position = 10 has name = SunPCSC Provider at position = 11 has name = SunMSCAPI Using JCE_PROVIDER for SecretKeyFactory ## = SunJCE Secret Code: [B@14d8444b Output on JDK 13-ea : D:\Java7Workspace\Tests\src>D:\jdk-13\bin\java -cp .;D:\bc-fips-1.0.1.jar JI9059298 Java SecurityProvider list: Provider at position = 1 has name = SUN Provider at position = 2 has name = SunRsaSign Provider at position = 3 has name = SunEC Provider at position = 4 has name = SunJSSE Provider at position = 5 has name = SunJCE Provider at position = 6 has name = SunJGSS Provider at position = 7 has name = SunSASL Provider at position = 8 has name = XMLDSig Provider at position = 9 has name = SunPCSC Provider at position = 10 has name = JdkLDAP Provider at position = 11 has name = JdkSASL Provider at position = 12 has name = SunMSCAPI Provider at position = 13 has name = SunPKCS11 Added Provider to Position 1: BCFIPS BouncyCastle FIPStatus: true BouncyCastle FIPS Approved Mode: true Java SecurityProvider list: Provider at position = 1 has name = BCFIPS Provider at position = 2 has name = SUN Provider at position = 3 has name = SunRsaSign Provider at position = 4 has name = SunEC Provider at position = 5 has name = SunJSSE Provider at position = 6 has name = SunJCE Provider at position = 7 has name = SunJGSS Provider at position = 8 has name = SunSASL Provider at position = 9 has name = XMLDSig Provider at position = 10 has name = SunPCSC Provider at position = 11 has name = JdkLDAP Provider at position = 12 has name = JdkSASL Provider at position = 13 has name = SunMSCAPI Provider at position = 14 has name = SunPKCS11 WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider$CoreSecureRandom (file:/D:/bc-fips-1.0.1.jar) to method sun.security.jca.Providers.getSunProvider() WARNING: Please consider reporting this to the maintainers of org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider$CoreSecureRandom WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release Using JCE_PROVIDER for SecretKeyFactory ## = SunJCE org.bouncycastle.crypto.IllegalKeyException: Key size for HMAC must be at least 112 bits in approved mode: SHA-1/HMAC at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source) at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source) at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source) at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source) at org.bouncycastle.jcajce.provider.BaseHMac.engineInit(Unknown Source) at java.base/javax.crypto.Mac.init(Mac.java:433) at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.deriveKey(PBKDF2KeyImpl.java:182) at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.<init>(PBKDF2KeyImpl.java:122) at java.base/com.sun.crypto.provider.PBKDF2Core.engineGenerateSecret(PBKDF2Core.java:69) at java.base/javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:338) at JI9059298.pbkdf2Encryption(JI9059298.java:65) at JI9059298.generateSecret(JI9059298.java:48) at JI9059298.generateSecretCode(JI9059298.java:36) at JI9059298.main(JI9059298.java:25) Exception in thread "main" org.bouncycastle.crypto.IllegalKeyException: Key size for HMAC must be at least 112 bits in approved mode: SHA-1/HMAC at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source) at org.bouncycastle.crypto.fips.FipsSHS$MACOperatorFactory.createMAC(Unknown Source) at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source) at org.bouncycastle.crypto.fips.FipsMACOperatorFactory.createOutputMACCalculator(Unknown Source) at org.bouncycastle.jcajce.provider.BaseHMac.engineInit(Unknown Source) at java.base/javax.crypto.Mac.init(Mac.java:433) at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.deriveKey(PBKDF2KeyImpl.java:182) at java.base/com.sun.crypto.provider.PBKDF2KeyImpl.<init>(PBKDF2KeyImpl.java:122) at java.base/com.sun.crypto.provider.PBKDF2Core.engineGenerateSecret(PBKDF2Core.java:69) at java.base/javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:338) at JI9059298.pbkdf2Encryption(JI9059298.java:65) at JI9059298.generateSecret(JI9059298.java:48) at JI9059298.generateSecretCode(JI9059298.java:36) at JI9059298.main(JI9059298.java:25)
11-02-2019