mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix code
This commit is contained in:
parent
49a6611ae8
commit
edbd5140aa
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.core.io;
|
package org.dromara.hutool.core.io;
|
||||||
|
|
||||||
import org.dromara.hutool.core.exception.ExceptionUtil;
|
|
||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,24 +22,72 @@ import org.dromara.hutool.core.text.StrUtil;
|
|||||||
public class IORuntimeException extends RuntimeException {
|
public class IORuntimeException extends RuntimeException {
|
||||||
private static final long serialVersionUID = 8247610319171014183L;
|
private static final long serialVersionUID = 8247610319171014183L;
|
||||||
|
|
||||||
public IORuntimeException(final Throwable e) {
|
/**
|
||||||
super(ExceptionUtil.getMessage(e), e);
|
* 构造
|
||||||
|
*/
|
||||||
|
public IORuntimeException() {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param e 异常
|
||||||
|
*/
|
||||||
|
public IORuntimeException(final Throwable e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param message 消息
|
||||||
|
*/
|
||||||
public IORuntimeException(final String message) {
|
public IORuntimeException(final String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param messageTemplate 消息模板
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
public IORuntimeException(final String messageTemplate, final Object... params) {
|
public IORuntimeException(final String messageTemplate, final Object... params) {
|
||||||
super(StrUtil.format(messageTemplate, params));
|
super(StrUtil.format(messageTemplate, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IORuntimeException(final String message, final Throwable throwable) {
|
/**
|
||||||
super(message, throwable);
|
* 构造
|
||||||
|
*
|
||||||
|
* @param message 消息
|
||||||
|
* @param cause 被包装的子异常
|
||||||
|
*/
|
||||||
|
public IORuntimeException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IORuntimeException(final Throwable throwable, final String messageTemplate, final Object... params) {
|
/**
|
||||||
super(StrUtil.format(messageTemplate, params), throwable);
|
* 构造
|
||||||
|
*
|
||||||
|
* @param message 消息
|
||||||
|
* @param cause 被包装的子异常
|
||||||
|
* @param enableSuppression 是否启用抑制
|
||||||
|
* @param writableStackTrace 堆栈跟踪是否应该是可写的
|
||||||
|
*/
|
||||||
|
public IORuntimeException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param cause 被包装的子异常
|
||||||
|
* @param messageTemplate 消息模板
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
|
public IORuntimeException(final Throwable cause, final String messageTemplate, final Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params), cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
|
||||||
* Hutool is licensed under Mulan PSL v2.
|
|
||||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
* You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
* http://license.coscl.org.cn/MulanPSL2
|
|
||||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
||||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
||||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the Mulan PSL v2 for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.dromara.hutool.crypto.openssl;
|
|
||||||
|
|
||||||
import org.dromara.hutool.crypto.KeyUtil;
|
|
||||||
import org.dromara.hutool.crypto.SecureUtil;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.spec.PBEKeySpec;
|
|
||||||
import javax.crypto.spec.PBEParameterSpec;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class OpenSSLPBECommon {
|
|
||||||
protected static final int SALT_SIZE_BYTES = 8;
|
|
||||||
/**
|
|
||||||
* OpenSSL's magic initial bytes.
|
|
||||||
*/
|
|
||||||
protected static final byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
|
|
||||||
protected static final String OPENSSL_HEADER_ENCODE = "ASCII";
|
|
||||||
|
|
||||||
protected static Cipher initializeCipher(char[] password, byte[] salt, int cipherMode,
|
|
||||||
final String algorithm, int iterationCount)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
final Cipher cipher = SecureUtil.createCipher(algorithm);
|
|
||||||
|
|
||||||
final SecretKey key = KeyUtil.generateKey(algorithm, new PBEKeySpec(password));
|
|
||||||
cipher.init(cipherMode, key, new PBEParameterSpec(salt, iterationCount));
|
|
||||||
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,97 +12,52 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.crypto.openssl;
|
package org.dromara.hutool.crypto.openssl;
|
||||||
|
|
||||||
import org.dromara.hutool.core.array.ArrayUtil;
|
import org.dromara.hutool.crypto.CryptoException;
|
||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.crypto.KeyUtil;
|
||||||
|
import org.dromara.hutool.crypto.SecureUtil;
|
||||||
|
import org.dromara.hutool.crypto.SpecUtil;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.CipherInputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
source: http://stackoverflow.com/questions/11783062/how-to-decrypt-an-encrypted-file-in-java-with-openssl-with-aes
|
* OpenSSL风格的PBE输入流,用于密文解密
|
||||||
*/
|
* 来自:http://stackoverflow.com/questions/11783062/how-to-decrypt-an-encrypted-file-in-java-with-openssl-with-aes
|
||||||
public class OpenSSLPBEInputStream extends InputStream {
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class OpenSSLPBEInputStream extends CipherInputStream {
|
||||||
|
|
||||||
protected final static int READ_BLOCK_SIZE = 64 * 1024;
|
/**
|
||||||
|
* 构造
|
||||||
private final Cipher cipher;
|
*
|
||||||
private final InputStream inStream;
|
* @param in 流
|
||||||
|
* @param algorithm 算法
|
||||||
private final byte[] bufferCipher = new byte[READ_BLOCK_SIZE];
|
* @param iterationCount 摘要次数
|
||||||
private byte[] bufferClear = null;
|
* @param password 口令
|
||||||
|
*/
|
||||||
private int index = Integer.MAX_VALUE;
|
public OpenSSLPBEInputStream(final InputStream in,
|
||||||
private int maxIndex = 0;
|
|
||||||
|
|
||||||
public OpenSSLPBEInputStream(final InputStream streamIn,
|
|
||||||
final String algorithm,
|
final String algorithm,
|
||||||
final int iterationCount,
|
final int iterationCount,
|
||||||
final char[] password) throws IOException {
|
final char[] password) {
|
||||||
|
|
||||||
this.inStream = streamIn;
|
super(in, createDecryptCipher(algorithm,
|
||||||
|
SaltMagic.getSalt(in), iterationCount, password));
|
||||||
readHeader();
|
}
|
||||||
final byte[] salt = readSalt();
|
|
||||||
|
|
||||||
|
private static Cipher createDecryptCipher(final String algorithm, final byte[] salt,
|
||||||
|
final int iterationCount,
|
||||||
|
final char[] password) {
|
||||||
|
final Cipher cipher = SecureUtil.createCipher(algorithm);
|
||||||
try {
|
try {
|
||||||
this.cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.DECRYPT_MODE, algorithm, iterationCount);
|
cipher.init(Cipher.DECRYPT_MODE,
|
||||||
|
KeyUtil.generatePBEKey(algorithm, password),
|
||||||
|
SpecUtil.createPBEParameterSpec(salt, iterationCount));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
throw new IOException(e);
|
throw new CryptoException(e);
|
||||||
}
|
}
|
||||||
}
|
return cipher;
|
||||||
|
|
||||||
@Override
|
|
||||||
public int available() throws IOException {
|
|
||||||
return inStream.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
if (maxIndex < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (index > maxIndex) {
|
|
||||||
index = 0;
|
|
||||||
final int read = inStream.read(bufferCipher);
|
|
||||||
|
|
||||||
if (read != -1) {
|
|
||||||
bufferClear = cipher.update(bufferCipher, 0, read);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read == -1 || bufferClear == null || bufferClear.length == 0) {
|
|
||||||
try {
|
|
||||||
bufferClear = cipher.doFinal();
|
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
|
||||||
bufferClear = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maxIndex = bufferClear == null ? -1 : bufferClear.length - 1;
|
|
||||||
|
|
||||||
if (maxIndex < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bufferClear[index++] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readHeader() throws IOException {
|
|
||||||
final byte[] headerBytes = new byte[OpenSSLPBECommon.SALTED_MAGIC.length];
|
|
||||||
inStream.read(headerBytes);
|
|
||||||
|
|
||||||
if (!Arrays.equals(OpenSSLPBECommon.SALTED_MAGIC, headerBytes)) {
|
|
||||||
throw new IOException("unexpected file header " + StrUtil.utf8Str(headerBytes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readSalt() throws IOException {
|
|
||||||
final byte[] salt = new byte[OpenSSLPBECommon.SALT_SIZE_BYTES];
|
|
||||||
inStream.read(salt);
|
|
||||||
return salt;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.crypto.openssl;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.io.IORuntimeException;
|
||||||
|
import org.dromara.hutool.core.util.RandomUtil;
|
||||||
|
import org.dromara.hutool.crypto.CryptoException;
|
||||||
|
import org.dromara.hutool.crypto.KeyUtil;
|
||||||
|
import org.dromara.hutool.crypto.SecureUtil;
|
||||||
|
import org.dromara.hutool.crypto.SpecUtil;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenSSL风格的PBE输出流,用于生成密文
|
||||||
|
* 来自:http://stackoverflow.com/questions/11783062/how-to-decrypt-an-encrypted-file-in-java-with-openssl-with-aes
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class OpenSSLPBEOutputStream extends CipherOutputStream {
|
||||||
|
protected static final int WRITE_BLOCK_SIZE = 64 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param out 流
|
||||||
|
* @param algorithm 算法
|
||||||
|
* @param iterationCount 摘要次数
|
||||||
|
* @param password 口令
|
||||||
|
*/
|
||||||
|
public OpenSSLPBEOutputStream(final OutputStream out,
|
||||||
|
final String algorithm,
|
||||||
|
final int iterationCount,
|
||||||
|
final char[] password) {
|
||||||
|
super(out, createEncryptCipher(algorithm, writeRandomHeader(out),
|
||||||
|
iterationCount, password));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] writeRandomHeader(final OutputStream out) throws IORuntimeException {
|
||||||
|
final byte[] salt = RandomUtil.randomBytes(SaltMagic.SALT_LEN);
|
||||||
|
try {
|
||||||
|
out.write(SaltMagic.SALTED_MAGIC);
|
||||||
|
out.write(salt);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
return salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Cipher createEncryptCipher(final String algorithm, final byte[] salt,
|
||||||
|
final int iterationCount,
|
||||||
|
final char[] password) {
|
||||||
|
final Cipher cipher = SecureUtil.createCipher(algorithm);
|
||||||
|
try {
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE,
|
||||||
|
KeyUtil.generatePBEKey(algorithm, password),
|
||||||
|
SpecUtil.createPBEParameterSpec(salt, iterationCount));
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new CryptoException(e);
|
||||||
|
}
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
}
|
@ -10,15 +10,13 @@
|
|||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.dromara.hutool.crypto.symmetric;
|
package org.dromara.hutool.crypto.openssl;
|
||||||
|
|
||||||
import org.dromara.hutool.core.array.ArrayUtil;
|
|
||||||
import org.dromara.hutool.core.lang.Assert;
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
import org.dromara.hutool.core.util.ByteUtil;
|
import org.dromara.hutool.core.util.ByteUtil;
|
||||||
import org.dromara.hutool.crypto.digest.MD5;
|
import org.dromara.hutool.crypto.digest.MD5;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@ -34,74 +32,6 @@ import java.util.Arrays;
|
|||||||
* @since 6.0.0
|
* @since 6.0.0
|
||||||
*/
|
*/
|
||||||
public class OpenSSLSaltParser {
|
public class OpenSSLSaltParser {
|
||||||
private final static byte SALT_LEN = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OpenSSL's magic initial bytes.
|
|
||||||
*/
|
|
||||||
private static final byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取魔术值和随机盐的长度:16(128位)
|
|
||||||
*/
|
|
||||||
public static final int MAGIC_SALT_LENGTH = SALTED_MAGIC.length + SALT_LEN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取去除头部盐的加密数据<br>
|
|
||||||
*
|
|
||||||
* @param encryptedData 密文
|
|
||||||
* @return 实际密文
|
|
||||||
*/
|
|
||||||
public static byte[] getData(final byte[] encryptedData) {
|
|
||||||
if (ArrayUtil.startWith(encryptedData, SALTED_MAGIC)) {
|
|
||||||
return Arrays.copyOfRange(encryptedData, SALTED_MAGIC.length + SALT_LEN, encryptedData.length);
|
|
||||||
}
|
|
||||||
return encryptedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取8位salt随机数<br>
|
|
||||||
*
|
|
||||||
* @param encryptedData 密文
|
|
||||||
* @return salt随机数
|
|
||||||
*/
|
|
||||||
public static byte[] getSalt(final byte[] encryptedData) {
|
|
||||||
if (ArrayUtil.startWith(encryptedData, SALTED_MAGIC)) {
|
|
||||||
return Arrays.copyOfRange(encryptedData, SALTED_MAGIC.length, MAGIC_SALT_LENGTH);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为加密后的数据添加Magic头,生成的密文格式为:
|
|
||||||
* <pre>
|
|
||||||
* Salted__[salt][data]
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @param data 数据
|
|
||||||
* @param salt 加盐值,必须8位,{@code null}表示返回原文
|
|
||||||
* @return 密文
|
|
||||||
*/
|
|
||||||
public static byte[] addMagic(final byte[] data, final byte[] salt) {
|
|
||||||
if (null == salt) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
Assert.isTrue(SALT_LEN == salt.length);
|
|
||||||
return ByteUtil.concat(SALTED_MAGIC, salt, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取Magic头,生成的密文格式为:
|
|
||||||
* <pre>
|
|
||||||
* Salted__[salt]
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @param salt 加盐值,必须8位,不能为{@code null}
|
|
||||||
* @return Magic头
|
|
||||||
*/
|
|
||||||
public static byte[] getSaltedMagic(final byte[] salt) {
|
|
||||||
return ByteUtil.concat(SALTED_MAGIC, salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建MD5 OpenSSLSaltParser
|
* 创建MD5 OpenSSLSaltParser
|
||||||
@ -175,7 +105,7 @@ public class OpenSSLSaltParser {
|
|||||||
keyAndIvResult[0] = pass;
|
keyAndIvResult[0] = pass;
|
||||||
return keyAndIvResult;
|
return keyAndIvResult;
|
||||||
}
|
}
|
||||||
Assert.isTrue(SALT_LEN == salt.length);
|
Assert.isTrue(SaltMagic.SALT_LEN == salt.length);
|
||||||
|
|
||||||
final byte[] passAndSalt = ByteUtil.concat(pass, salt);
|
final byte[] passAndSalt = ByteUtil.concat(pass, salt);
|
||||||
|
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.crypto.openssl;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.array.ArrayUtil;
|
||||||
|
import org.dromara.hutool.core.io.IORuntimeException;
|
||||||
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
import org.dromara.hutool.core.util.ByteUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加盐值魔数<br>
|
||||||
|
* 用于在OpenSSL生成的密文中,提取加盐值等相关信息
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class SaltMagic {
|
||||||
|
/**
|
||||||
|
* 加盐值长度
|
||||||
|
*/
|
||||||
|
public final static byte SALT_LEN = 8;
|
||||||
|
/**
|
||||||
|
* OpenSSL's magic initial bytes.
|
||||||
|
*/
|
||||||
|
public static final byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取魔术值和随机盐的长度:16(128位)
|
||||||
|
*/
|
||||||
|
public static final int MAGIC_SALT_LENGTH = SALTED_MAGIC.length + SALT_LEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取去除头部盐的加密数据<br>
|
||||||
|
*
|
||||||
|
* @param encryptedData 密文
|
||||||
|
* @return 实际密文
|
||||||
|
*/
|
||||||
|
public static byte[] getData(final byte[] encryptedData) {
|
||||||
|
if (ArrayUtil.startWith(encryptedData, SALTED_MAGIC)) {
|
||||||
|
return Arrays.copyOfRange(encryptedData, SALTED_MAGIC.length + SALT_LEN, encryptedData.length);
|
||||||
|
}
|
||||||
|
return encryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取流中的加盐值<br>
|
||||||
|
* 不关闭流
|
||||||
|
*
|
||||||
|
* @param in 流
|
||||||
|
* @return salt
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
*/
|
||||||
|
public static byte[] getSalt(final InputStream in) throws IORuntimeException {
|
||||||
|
final byte[] headerBytes = new byte[SALTED_MAGIC.length];
|
||||||
|
|
||||||
|
try{
|
||||||
|
final int readHeaderSize = in.read(headerBytes);
|
||||||
|
if (readHeaderSize < headerBytes.length ||
|
||||||
|
!Arrays.equals(SALTED_MAGIC, headerBytes)) {
|
||||||
|
throw new IORuntimeException("Unexpected magic header " + StrUtil.utf8Str(headerBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] salt = new byte[SALT_LEN];
|
||||||
|
final int readSaltSize = in.read(salt);
|
||||||
|
if (readSaltSize < salt.length) {
|
||||||
|
throw new IORuntimeException("Unexpected salt: " + StrUtil.utf8Str(salt));
|
||||||
|
}
|
||||||
|
return salt;
|
||||||
|
} catch (final IOException e){
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取8位salt随机数<br>
|
||||||
|
*
|
||||||
|
* @param encryptedData 密文
|
||||||
|
* @return salt随机数
|
||||||
|
*/
|
||||||
|
public static byte[] getSalt(final byte[] encryptedData) {
|
||||||
|
if (ArrayUtil.startWith(encryptedData, SALTED_MAGIC)) {
|
||||||
|
return Arrays.copyOfRange(encryptedData, SALTED_MAGIC.length, MAGIC_SALT_LENGTH);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为加密后的数据添加Magic头,生成的密文格式为:
|
||||||
|
* <pre>
|
||||||
|
* Salted__[salt][data]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @param salt 加盐值,必须8位,{@code null}表示返回原文
|
||||||
|
* @return 密文
|
||||||
|
*/
|
||||||
|
public static byte[] addMagic(final byte[] data, final byte[] salt) {
|
||||||
|
if (null == salt) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
Assert.isTrue(SALT_LEN == salt.length);
|
||||||
|
return ByteUtil.concat(SALTED_MAGIC, salt, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Magic头,生成的密文格式为:
|
||||||
|
* <pre>
|
||||||
|
* Salted__[salt]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param salt 加盐值,必须8位,不能为{@code null}
|
||||||
|
* @return Magic头
|
||||||
|
*/
|
||||||
|
public static byte[] getSaltedMagic(final byte[] salt) {
|
||||||
|
return ByteUtil.concat(SALTED_MAGIC, salt);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenSSL相关封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
package org.dromara.hutool.crypto.openssl;
|
@ -21,6 +21,8 @@ import org.dromara.hutool.core.lang.Opt;
|
|||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
import org.dromara.hutool.core.util.RandomUtil;
|
import org.dromara.hutool.core.util.RandomUtil;
|
||||||
import org.dromara.hutool.crypto.*;
|
import org.dromara.hutool.crypto.*;
|
||||||
|
import org.dromara.hutool.crypto.openssl.OpenSSLSaltParser;
|
||||||
|
import org.dromara.hutool.crypto.openssl.SaltMagic;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
import javax.crypto.CipherInputStream;
|
||||||
@ -311,7 +313,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
|
|||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
return OpenSSLSaltParser.addMagic(result, salt);
|
return SaltMagic.addMagic(result, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -358,11 +360,11 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
|
|||||||
final byte[] decryptData;
|
final byte[] decryptData;
|
||||||
|
|
||||||
lock.lock();
|
lock.lock();
|
||||||
final byte[] salt = OpenSSLSaltParser.getSalt(bytes);
|
final byte[] salt = SaltMagic.getSalt(bytes);
|
||||||
try {
|
try {
|
||||||
final Cipher cipher = initMode(Cipher.DECRYPT_MODE, salt);
|
final Cipher cipher = initMode(Cipher.DECRYPT_MODE, salt);
|
||||||
blockSize = cipher.getBlockSize();
|
blockSize = cipher.getBlockSize();
|
||||||
decryptData = cipher.doFinal(OpenSSLSaltParser.getData(bytes));
|
decryptData = cipher.doFinal(SaltMagic.getData(bytes));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
throw new CryptoException(e);
|
throw new CryptoException(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -10,22 +10,20 @@
|
|||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.dromara.hutool.crypto.symmetric;
|
package org.dromara.hutool.crypto.openssl;
|
||||||
|
|
||||||
import org.dromara.hutool.core.codec.binary.Base64;
|
import org.dromara.hutool.core.codec.binary.Base64;
|
||||||
import org.dromara.hutool.core.io.IoUtil;
|
import org.dromara.hutool.core.io.IoUtil;
|
||||||
import org.dromara.hutool.core.lang.Console;
|
|
||||||
import org.dromara.hutool.crypto.KeyUtil;
|
import org.dromara.hutool.crypto.KeyUtil;
|
||||||
import org.dromara.hutool.crypto.SecureUtil;
|
import org.dromara.hutool.crypto.SecureUtil;
|
||||||
import org.dromara.hutool.crypto.openssl.OpenSSLPBEInputStream;
|
import org.dromara.hutool.crypto.symmetric.SymmetricCrypto;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class SaltUtilTest {
|
public class SaltTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试:
|
* 测试:
|
||||||
@ -35,7 +33,7 @@ public class SaltUtilTest {
|
|||||||
void rc4Test() {
|
void rc4Test() {
|
||||||
final String encrypted = "U2FsdGVkX19DSROPe0+Ejkw84osqWw==";
|
final String encrypted = "U2FsdGVkX19DSROPe0+Ejkw84osqWw==";
|
||||||
|
|
||||||
final byte[] salt = OpenSSLSaltParser.getSalt(SecureUtil.decode(encrypted));
|
final byte[] salt = SaltMagic.getSalt(SecureUtil.decode(encrypted));
|
||||||
Assertions.assertNotNull(salt);
|
Assertions.assertNotNull(salt);
|
||||||
|
|
||||||
final byte[][] keyAndIV = OpenSSLSaltParser.ofMd5(32, "RC4")
|
final byte[][] keyAndIV = OpenSSLSaltParser.ofMd5(32, "RC4")
|
||||||
@ -46,7 +44,7 @@ public class SaltUtilTest {
|
|||||||
final SecretKey rc4Key = KeyUtil.generateKey("RC4", keyAndIV[0]);
|
final SecretKey rc4Key = KeyUtil.generateKey("RC4", keyAndIV[0]);
|
||||||
Assertions.assertNotNull(rc4Key);
|
Assertions.assertNotNull(rc4Key);
|
||||||
|
|
||||||
final byte[] data = OpenSSLSaltParser.getData(SecureUtil.decode(encrypted));
|
final byte[] data = SaltMagic.getData(SecureUtil.decode(encrypted));
|
||||||
|
|
||||||
final SymmetricCrypto rc4 = new SymmetricCrypto("RC4", rc4Key);
|
final SymmetricCrypto rc4 = new SymmetricCrypto("RC4", rc4Key);
|
||||||
final String decrypt = rc4.decryptStr(data);
|
final String decrypt = rc4.decryptStr(data);
|
||||||
@ -83,7 +81,7 @@ public class SaltUtilTest {
|
|||||||
* https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
|
* https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void aesTest2() throws IOException {
|
void aesUseStreamTest() {
|
||||||
final String encrypted = "U2FsdGVkX1+lqsuKAR+OdOeNduvx5wgXf6yEUdDIh3g=";
|
final String encrypted = "U2FsdGVkX1+lqsuKAR+OdOeNduvx5wgXf6yEUdDIh3g=";
|
||||||
final String pass = "1234567890123456";
|
final String pass = "1234567890123456";
|
||||||
final String algorithm = "PBEWITHMD5AND256BITAES-CBC-OPENSSL";
|
final String algorithm = "PBEWITHMD5AND256BITAES-CBC-OPENSSL";
|
Loading…
x
Reference in New Issue
Block a user