Files
hutool/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/RC4.java
zhuqianchao 0e9909ffeb 1. 在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。
说明一:如果在lock方法与try代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功获取锁。
说明二:如果lock方法在try代码块之内,可能由于其它方法抛出异常,导致在finally代码块中,unlock对未加锁的对象解锁,它会调用AQS的tryRelease方法(取决于具体实现类),抛出IllegalMonitorStateException异常。
说明三:在Lock对象的lock方法实现中可能抛出unchecked异常,产生的后果与说明二相同。 java.concurrent.LockShouldWithTryFinallyRule.rule.desc

2. 补上遗漏的Override注解
2020-08-29 17:22:36 +08:00

219 lines
5.5 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cn.hutool.crypto.symmetric;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.CryptoException;
/**
* RC4加密解密算法实现<br>
* 来自https://github.com/xSAVIKx/RC4-cipher/blob/master/src/main/java/com/github/xsavikx/rc4/RC4.java
*
* @author Iurii SergiichukLooly
*/
public class RC4 implements Serializable {
private static final long serialVersionUID = 1L;
private static final int SBOX_LENGTH = 256;
/** 密钥最小长度 */
private static final int KEY_MIN_LENGTH = 5;
/** Sbox */
private int[] sbox;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 构造
*
* @param key 密钥
* @throws CryptoException key长度小于5或者大于255抛出此异常
*/
public RC4(String key) throws CryptoException {
setKey(key);
}
/**
* 加密
*
* @param message 消息
* @param charset 编码
* @return 密文
* @throws CryptoException key长度小于5或者大于255抛出此异常
*/
public byte[] encrypt(String message, Charset charset) throws CryptoException {
return crypt(StrUtil.bytes(message, charset));
}
/**
* 加密使用默认编码UTF-8
*
* @param message 消息
* @return 密文
* @throws CryptoException key长度小于5或者大于255抛出此异常
*/
public byte[] encrypt(String message) throws CryptoException {
return encrypt(message, CharsetUtil.CHARSET_UTF_8);
}
/**
* 加密
*
* @param data 数据
* @return 加密后的Hex
* @since 4.5.12
*/
public String encryptHex(byte[] data) {
return HexUtil.encodeHexStr(crypt(data));
}
/**
* 加密
*
* @param data 数据
* @return 加密后的Base64
* @since 4.5.12
*/
public String encryptBase64(byte[] data) {
return Base64.encode(crypt(data));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Hex
* @since 4.5.12
*/
public String encryptHex(String data, Charset charset) {
return HexUtil.encodeHexStr(encrypt(data, charset));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param charset 编码
* @return 加密后的Base64
* @since 4.5.12
*/
public String encryptBase64(String data, Charset charset) {
return Base64.encode(encrypt(data, charset));
}
/**
* 解密
*
* @param message 消息
* @param charset 编码
* @return 明文
* @throws CryptoException key长度小于5或者大于255抛出此异常
*/
public String decrypt(byte[] message, Charset charset) throws CryptoException {
return StrUtil.str(crypt(message), charset);
}
/**
* 解密使用默认编码UTF-8
*
* @param message 消息
* @return 明文
* @throws CryptoException key长度小于5或者大于255抛出此异常
*/
public String decrypt(byte[] message) throws CryptoException {
return decrypt(message, CharsetUtil.CHARSET_UTF_8);
}
/**
* 加密或解密指定值,调用此方法前需初始化密钥
*
* @param msg 要加密或解密的消息
* @return 加密或解密后的值
*/
public byte[] crypt(final byte[] msg) {
final ReadLock readLock = this.lock.readLock();
byte[] code;
readLock.lock();
try {
final int[] sbox = this.sbox.clone();
code = new byte[msg.length];
int i = 0;
int j = 0;
for (int n = 0; n < msg.length; n++) {
i = (i + 1) % SBOX_LENGTH;
j = (j + sbox[i]) % SBOX_LENGTH;
swap(i, j, sbox);
int rand = sbox[(sbox[i] + sbox[j]) % SBOX_LENGTH];
code[n] = (byte) (rand ^ msg[n]);
}
} finally {
readLock.unlock();
}
return code;
}
/**
* 设置密钥
*
* @param key 密钥
* @throws CryptoException key长度小于5或者大于255抛出此异常
*/
public void setKey(String key) throws CryptoException {
final int length = key.length();
if (length < KEY_MIN_LENGTH || length >= SBOX_LENGTH) {
throw new CryptoException("Key length has to be between {} and {}", KEY_MIN_LENGTH, (SBOX_LENGTH - 1));
}
final WriteLock writeLock = this.lock.writeLock();
writeLock.lock();
try {
this.sbox = initSBox(StrUtil.utf8Bytes(key));
} finally {
writeLock.unlock();
}
}
//----------------------------------------------------------------------------------------------------------------------- Private method start
/**
* 初始化Sbox
*
* @param key 密钥
* @return sbox
*/
private int[] initSBox(byte[] key) {
int[] sbox = new int[SBOX_LENGTH];
int j = 0;
for (int i = 0; i < SBOX_LENGTH; i++) {
sbox[i] = i;
}
for (int i = 0; i < SBOX_LENGTH; i++) {
j = (j + sbox[i] + (key[i % key.length]) & 0xFF) % SBOX_LENGTH;
swap(i, j, sbox);
}
return sbox;
}
/**
* 交换指定两个位置的值
*
* @param i 位置1
* @param j 位置2
* @param sbox 数组
*/
private void swap(int i, int j, int[] sbox) {
int temp = sbox[i];
sbox[i] = sbox[j];
sbox[j] = temp;
}
//----------------------------------------------------------------------------------------------------------------------- Private method end
}