AES secret key length error: java.security.InvalidKeyException: Illegal key size or default parameters [Solved]

Preface:

Encounter the following problems, the result of Baidu when a solution, here to their research out of the content together to survive, hope to be useful for later generations

The stacktrace of this question:

java.security.InvalidKeyException: Illegal key size or default parameters
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1011)
                at javax.crypto.Cipher.implInit(Cipher.java:786)
                at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
                at javax.crypto.Cipher.init(Cipher.java:1213)
                at javax.crypto.Cipher.init(Cipher.java:1153)

Baidu comes out with the same thing. The United States restricts technology exports and forbids AES secret keys above 128. Therefore, ORALCE makes two policy files to restrict it. To do this, you need to download two new policy files from Oracle. At first glance, it looks good. In fact, it’s a bit lame. In order to use an algorithm, it has to add an uncontrollable operation and maintenance work

In fact, there are other solutions for this thing, and there are more than one. The key is to use your brain

Use other resource packages

The restriction is given by JDK. Let’s change the package used for OK encryption and decryption. Instead of JDK content, change it to Apache commons crypto

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-crypto</artifactId>
    <version>1.0.0</version>
</dependency>

But this product has a limitation. It’s nothing on Linux. You need to install OpenSSL on windows. If you really want to use it, please install SSL by yourself (it’s a little complicated, because you don’t want to increase the difficulty of developers, so you don’t use this method)

Corresponding interface code:

  /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.commons.crypto.examples;
  
  import java.nio.charset.StandardCharsets;
  import java.util.Arrays;
  import java.util.Properties;
  
  import javax.crypto.Cipher;
  import javax.crypto.spec.IvParameterSpec;
  import javax.crypto.spec.SecretKeySpec;
  
  import org.apache.commons.crypto.cipher.CryptoCipher;
  import org.apache.commons.crypto.cipher.CryptoCipherFactory;
  import org.apache.commons.crypto.cipher.CryptoCipherFactory.CipherProvider;
  import org.apache.commons.crypto.utils.Utils;
  
  /**
   * Example showing use of the CryptoCipher API using a byte array
   */
  public class CipherByteArrayExample {
  
      public static void main(String[] args) throws Exception {
  
          final SecretKeySpec key = new SecretKeySpec(getUTF8Bytes("1234567890123456"),"AES");
          final IvParameterSpec iv = new IvParameterSpec(getUTF8Bytes("1234567890123456"));
  
          Properties properties = new Properties();
          properties.setProperty(CryptoCipherFactory.CLASSES_KEY, CipherProvider.OPENSSL.getClassName());
          //Creates a CryptoCipher instance with the transformation and properties.
          final String transform = "AES/CBC/PKCS5Padding";
          CryptoCipher encipher = Utils.getCipherInstance(transform, properties);
          System.out.println("Cipher:  " + encipher.getClass().getCanonicalName());
  
          final String sampleInput = "hello world!";
          System.out.println("input:  " + sampleInput);
  
          byte[] input = getUTF8Bytes(sampleInput);
          byte[] output = new byte[32];
  
          //Initializes the cipher with ENCRYPT_MODE, key and iv.
          encipher.init(Cipher.ENCRYPT_MODE, key, iv);
          //Continues a multiple-part encryption/decryption operation for byte array.
          int updateBytes = encipher.update(input, 0, input.length, output, 0);
          System.out.println(updateBytes);
          //We must call doFinal at the end of encryption/decryption.
          int finalBytes = encipher.doFinal(input, 0, 0, output, updateBytes);
          System.out.println(finalBytes);
          //Closes the cipher.
          encipher.close();
  
          System.out.println(Arrays.toString(Arrays.copyOf(output, updateBytes+finalBytes)));
  
          // Now reverse the process using a different implementation with the same settings
          properties.setProperty(CryptoCipherFactory.CLASSES_KEY, CipherProvider.JCE.getClassName());
          CryptoCipher decipher = Utils.getCipherInstance(transform, properties);
          System.out.println("Cipher:  " + encipher.getClass().getCanonicalName());
  
          decipher.init(Cipher.DECRYPT_MODE, key, iv);
          byte [] decoded = new byte[32];
          decipher.doFinal(output, 0, updateBytes + finalBytes, decoded, 0);
  
          System.out.println("output: " + new String(decoded, StandardCharsets.UTF_8));
      }
  
      /**
       * Converts String to UTF8 bytes
       *
       * @param input the input string
       * @return UTF8 bytes
       */
      private static byte[] getUTF8Bytes(String input) {
          return input.getBytes(StandardCharsets.UTF_8);
      }
  
  }

If you are in conflict with OpenSSL, consider Hacking:

Hack

Anyway, it’s Java. We can think of a way to replace Java at run time. So the solution is to change the restriction to no restriction by reflection, and put the following code on the class you need to call. In this way, when the JVM loads the class, it will help you modify the corresponding policy

The code is as follows:

	static {
		String errorString = "Failed manually overriding key-length permissions.";
		int newMaxKeyLength;
		try {
			if ((newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES")) < 256) {
				Class c = Class.forName("javax.crypto.CryptoAllPermissionCollection");
				Constructor con = c.getDeclaredConstructor();
				con.setAccessible(true);
				Object allPermissionCollection = con.newInstance();
				Field f = c.getDeclaredField("all_allowed");
				f.setAccessible(true);
				f.setBoolean(allPermissionCollection, true);
				c = Class.forName("javax.crypto.CryptoPermissions");
				con = c.getDeclaredConstructor();
				con.setAccessible(true);
				Object allPermissions = con.newInstance();
				f = c.getDeclaredField("perms");
				f.setAccessible(true);
				((Map) f.get(allPermissions)).put("*", allPermissionCollection);
				c = Class.forName("javax.crypto.JceSecurityManager");
				f = c.getDeclaredField("defaultPolicy");
				f.setAccessible(true);
				Field mf = Field.class.getDeclaredField("modifiers");
				mf.setAccessible(true);
				mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
				f.set(null, allPermissions);
				newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
			}
		} catch (Exception e) {
			throw new RuntimeException(errorString, e);
		}
		if (newMaxKeyLength < 256)
			throw new RuntimeException(errorString); // hack failed
	}

Similar Posts: