Java等效于OpenSSL AES CBC加密

Java等效于OpenSSL AES CBC加密,java,encryption,openssl,cryptography,Java,Encryption,Openssl,Cryptography,我不是一个加密专家,特别是由于OpenSSL有很多缺少的文档,我不确定如何解决这个问题 我有一个外部系统,希望接收加密消息。提供的唯一示例以这种方式使用OpenSSL: $openssl enc-aes-256-cbc-a-in t.txt-k testpass U2FsdGVkX1/RUDASJKRxHv3ZuytSQWu5/AR2CKDLRnYH5GL4XRR4FGXKIWqks1 cQstcoSIgWfRPSOFj/5OtdNLeNXiVR6MxSKJ+NvS9LyUD8+Rg6XIcYU

我不是一个加密专家,特别是由于OpenSSL有很多缺少的文档,我不确定如何解决这个问题

我有一个外部系统,希望接收加密消息。提供的唯一示例以这种方式使用OpenSSL:

$openssl enc-aes-256-cbc-a-in t.txt-k testpass
U2FsdGVkX1/RUDASJKRxHv3ZuytSQWu5/AR2CKDLRnYH5GL4XRR4FGXKIWqks1
cQstcoSIgWfRPSOFj/5OtdNLeNXiVR6MxSKJ+NvS9LyUD8+Rg6XIcYUvxR4gHi3w
DWT44LAMCpRAh1Q0t4Z2g7rwb0D05T6ygLaWvB5zD/xGZD3brTqSlWmiJb9Imgda
M6SOZO7HBYDQEWQEUL5R6+EbkD21f6L3NX3hJFo+BJ+VFctiAlBO8NwT5l4ogo/s
GER8GQRR57XOX/kvKAimg==
其中
t.txt
文件在一行中包含此字符串:

AMOUNT=10&TID=19:23&CURRENCY=EUR&LANGUAGE=DE&SUCCESS\uURL=http://some.url/sucess&ERROR_URL=http://some.url/error&CONFIRMATION_URL=http://some.url/confirm&NAME=customer 全名`
我发现了其他问题,我能够使用以下代码进行加密:

String password=“passPhrase”;
String salt=“15charrandomsolt”;
int迭代次数=100;
/*根据给定的密码和salt派生密钥*/
SecretKeyFactory factory=SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA256”);
KeySpec spec=new-PBEKeySpec(password.tocharray(),salt.getBytes(Charset.forName(“UTF8”)),迭代次数,256次);
SecretKey tmp=工厂生成信任(规范);
SecretKey secret=newsecretkeyspec(tmp.getEncoded(),“AES”);
/*加密消息*/
Cipher Cipher=Cipher.getInstance(“AES/CBC/PKCS5Padding”);
cipher.init(cipher.ENCRYPT_模式,secret);
AlgorithmParameters params=cipher.getParameters();
字节[]iv=params.getParameterSpec(IvParameterSpec.class).getIV();
byte[]cipherText=cipher.doFinal(toBeEncrypted.getBytes(“UTF-8”);
encryptedData=Base64.getEncoder().encodeToString(密文);
encryptedData+=Base64.getEncoder().encodeToString(iv);
我不明白的是,我应该如何生成与OpenSSL类似的输出(encryptedData)。我有salt、iv和密文,OpenSSL输出Base64编码是这些的串联结果吗?还是只有一个

在加密之前,我与另一个系统共享的唯一内容是密码短语。如果他们不知道salt和迭代次数,他们如何解密结果

有人能回答这些未知参数,并告诉我上述代码是否与OpenSSL进程等效吗?

您可以考虑将密钥生成算法指定为两个MD5哈希的串联

关于这里提到的盐,政府说:

当使用salt时,加密数据的前八个字节 数据是为盐保留的:当 对文件进行加密并在加密后从中读取 解密


以下是解密上述OPENSSL加密的Java程序(需要Java 8):

导入java.nio.charset.StandardCharset;
导入java.nio.file.Files;
导入java.nio.file.path;
导入java.security.MessageDigest;
导入java.util.array;
导入java.util.Base64;
导入java.util.Base64.Decoder;
导入javax.crypto.Cipher;
导入javax.crypto.spec.IvParameterSpec;
导入javax.crypto.spec.SecretKeySpec;
公共类测试解密{
公共静态void main(最终字符串[]args)引发异常{
最终字节[]pass=“testpass”.getBytes(StandardCharsets.US\u ASCII);
最后一个字节[]magic=“Salted_uuuuuuuuuuu.”getBytes(StandardCharsets.US_ASCII);
最终字符串infle=“e:/t/e.txt”;
字符串源=新字符串(Files.readAllBytes(path.get(infle)),
标准字符集(US_ASCII);
source=source.replaceAll(“\\s”和“);
最终解码器=Base64.getDecoder();
最终字节[]inBytes=解码器。解码(源);
最后一个字节[]应该是magic=Arrays.copyOfRange(以字节为单位,0,
长度);
if(!array.equals(应该是magic,magic)){
System.out.println(“坏幻数”);
返回;
}
最后一个字节[]salt=Arrays.copyOfRange(inBytes,magic.length,
长度+8);
最后一个字节[]passAndSalt=concat(pass,salt);
字节[]哈希=新字节[0];
字节[]keyAndIv=新字节[0];
对于(int i=0;i<3;i++){
最终字节[]数据=concat(散列,passAndSalt);
final MessageDigest md=MessageDigest.getInstance(“MD5”);
hash=md.digest(数据);
keyAndIv=concat(keyAndIv,散列);
}
最后一个字节[]keyValue=Arrays.copyOfRange(keyAndIv,0,32);
最后一个字节[]iv=Arrays.copyOfRange(keyAndIv,32,48);
final Cipher=Cipher.getInstance(“AES/CBC/PKCS5Padding”);
最终SecretKeySpec密钥=新SecretKeySpec(keyValue,“AES”);
cipher.init(cipher.DECRYPT_模式,密钥,新的IvParameterSpec(iv));
final byte[]clear=cipher.doFinal(以字节为单位,16,以字节为单位,长度为-16);
最终字符串clearText=新字符串(clear,StandardCharsets.ISO_8859_1);
System.out.println(明文);
}
专用静态字节[]concat(最终字节[]a,最终字节[]b){
最终字节[]c=新字节[a.length+b.length];
数组复制(a,0,c,0,a.长度);
系统数组副本(b,0,c,a.长度,b.长度);
返回c;
}
}

这个问题有一个被接受的答案,这个答案有点陈旧,但这似乎是一个反复出现的问题。我有两个项目,我们与第三方通信,密码是OpenSSL AES,带有预共享密钥

我使用了尚未通用的ssl库。然而,它似乎停留在0.3.x版本,并且在近2年内没有发布任何版本,没有任何邮件列表流量或可见的开发,我不得不得出结论,这基本上已经死了

基于一些额外的stackoverflow问题,我确实发现了这两个问题,而且这两个问题似乎都是正确的
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class TestAesDecrypt {

    public static void main(final String[] args) throws Exception {
        final byte[] pass = "testpass".getBytes(StandardCharsets.US_ASCII);
        final byte[] magic = "Salted__".getBytes(StandardCharsets.US_ASCII);
        final String inFile = "e:/t/e.txt";

        String source = new String(Files.readAllBytes(Paths.get(inFile)),
                StandardCharsets.US_ASCII);
        source = source.replaceAll("\\s", "");
        final Decoder decoder = Base64.getDecoder();
        final byte[] inBytes = decoder.decode(source);

        final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0,
                magic.length);
        if (!Arrays.equals(shouldBeMagic, magic)) {
            System.out.println("Bad magic number");
            return;
        }

        final byte[] salt = Arrays.copyOfRange(inBytes, magic.length,
                magic.length + 8);

        final byte[] passAndSalt = concat(pass, salt);

        byte[] hash = new byte[0];
        byte[] keyAndIv = new byte[0];
        for (int i = 0; i < 3; i++) {
            final byte[] data = concat(hash, passAndSalt);
            final MessageDigest md = MessageDigest.getInstance("MD5");
            hash = md.digest(data);
            keyAndIv = concat(keyAndIv, hash);
        }

        final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
        final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
        final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
        final String clearText = new String(clear, StandardCharsets.ISO_8859_1);
        System.out.println(clearText);
    }

    private static byte[] concat(final byte[] a, final byte[] b) {
        final byte[] c = new byte[a.length + b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }
}
import groovy.transform.CompileStatic;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.SecureRandom;
import static java.nio.charset.StandardCharsets.*

/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a shared key (aka password) with symetric ciphers.
*/
@CompileStatic
class OpenSslAes {

/** OpenSSL's magic initial bytes. */
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);


static String encryptAndURLEncode(String password, String clearText) {
    String encrypted = encrypt(password, clearText);
    return URLEncoder.encode(encrypted, UTF_8.name() );
}

/**
 *
 * @param password  The password / key to encrypt with.
 * @param data      The data to encrypt
 * @return  A base64 encoded string containing the encrypted data.
 */
static String encrypt(String password, String clearText) {
    final byte[] pass = password.getBytes(US_ASCII);
    final byte[] salt = (new SecureRandom()).generateSeed(8);
    final byte[] inBytes = clearText.getBytes(UTF_8);

    final byte[] passAndSalt = array_concat(pass, salt);
    byte[] hash = new byte[0];
    byte[] keyAndIv = new byte[0];
    for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
        final byte[] hashData = array_concat(hash, passAndSalt);
        final MessageDigest md = MessageDigest.getInstance("MD5");
        hash = md.digest(hashData);
        keyAndIv = array_concat(keyAndIv, hash);
    }

    final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
    final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
    final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");

    final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
    byte[] data = cipher.doFinal(inBytes);
    data =  array_concat(array_concat(SALTED_MAGIC, salt), data);
    return Base64.getEncoder().encodeToString( data );
}

/**
 * @see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption  for what looks like a useful answer.  The not-yet-commons-ssl also has an implementation
 * @param password
 * @param source The encrypted data
 * @return
 */
static String decrypt(String password, String source) {
    final byte[] pass = password.getBytes(US_ASCII);

    final byte[] inBytes = Base64.getDecoder().decode(source);

    final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
    if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
        throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
    }

    final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);

    final byte[] passAndSalt = array_concat(pass, salt);

    byte[] hash = new byte[0];
    byte[] keyAndIv = new byte[0];
    for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
        final byte[] hashData = array_concat(hash, passAndSalt);
        final MessageDigest md = MessageDigest.getInstance("MD5");
        hash = md.digest(hashData);
        keyAndIv = array_concat(keyAndIv, hash);
    }

    final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
    final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");

    final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);

    final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
    return new String(clear, UTF_8);
}


private static byte[] array_concat(final byte[] a, final byte[] b) {
    final byte[] c = new byte[a.length + b.length];
    System.arraycopy(a, 0, c, 0, a.length);
    System.arraycopy(b, 0, c, a.length, b.length);
    return c;
}
}