aes key java_AES 秘钥长度问题:java.security.InvalidKeyException: Illegal key size or default parameters...

前言:

遇到下面的问题,结果百度的时候千篇一律的一种解决方案,这里把自己research出来的内容一并留存下来,希望对后来人有用。

这个问题的stacktrace:

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)

百度出来的东西都是一致的,美国限制技术出口,禁止128以上的AES秘钥,所以oralce搞了两个policy文件来限制。要做这个事情,就需要到oracle下载两个新的policy文件。乍看挺好,实际上比较扯,为了使用一个算法居然要增加一个不可控的运维工作。

然后实际上这个东西还是有其他解决方案的,而且不止一个,关键在于要肯动脑子:

使用其他资源包

限制是JDK给的,那么OK加解密使用的包我们换一下,不用JDK的内容,改成apache-commons-crypto

org.apache.commons

commons-crypto

1.0.0

但是这个货有个限制,在linux上面没啥,在windows上面需要你安装openssl,如果实在想要使用的请自行安装SSL(略复杂,由于不想增加开发人员的难度,所以没有采用这个方法)。

相应的接口代码:

/**

* 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);

}

}

如果你对于OPENSSL比较抵触,那么可以考虑hack一下:

Hack一下

无论如何它是java,java我们就可以想到办法在运行时替换,所以解决思路就是通过反射把限制修改为没有限制,把下面的代码放到你需要调用的类上,这样在jvm加载这个类的同时会帮你修改相应的policy策略

代码如下:

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

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值