Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8218723

Use SunJCE Mac in SecretKeyFactory PBKDF2 implementation

    Details

    • Subcomponent:
    • Introduced In Version:
      9
    • Resolved In Build:
      b13
    • CPU:
      x86_64
    • OS:
      windows_7

      Backports

        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


          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  jnimeh Jamil Nimeh
                  Reporter:
                  webbuggrp Webbug Group
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  3 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: