< 返回版块

ppluck 发表于 2024-09-14 16:50

Tags:Java,AES,ECB,Rust

背景

  • 我正在将Java8写的程序改造成Rust,其中有个AES加解密。
  • Java里已经证实是调用Cipher cipher = Cipher.getInstance("AES");生成的bytes,结果等效于Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
  • 即:采用了ECB模式,PKCS5Padding填充。
  • 期间GPT对我一顿忽悠,最后我让它下岗了

Java采用的是AES128

寻找加密结果一致的算法库

我在Rust里尝试寻找加密结果与Java加密结果一致的库:

  1. ring(使用起来好复杂,可查看前贴):自己加解密可以,无法与Java一致,应该是模式和填充的问题,但是不熟悉Rust库,也没找到合适的例子,真复杂。
  2. aes+ecb:Pkcs7填充,结果与Java不一致
  3. pkcs5:自己加解密可以,结果与Java不一致
  4. magic-crypt:自己加解密可以,结果与Java不一致
  5. aes+cbc:自己加解密可以,结果与Java不一致

证实源头默认加密算法

最后从Java入手,寻找一致的加解密算法,经过对比java的各种明确模式和填充模式的结果,寻找到了Java默认的算法: 确定了加密模式是ECB模式,PKCS5Padding填充。继续回归aes+ecb。



SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());

KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, random);

SecretKey secretKey = kgen.generateKey();

...

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

Cipher cipher = Cipher.getInstance("AES/OFB/PKCS5Padding");

Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5Padding");

Cipher cipher = Cipher.getInstance("AES");

...


最后结论:AES128 + ECB + PKCS5Padding。

限定Rust库范围寻找这几种模式进行解密验证

用Rust的aes+ecb库进行解密验证:查看了ecb的源码,有这几种填充模式:

  • NoPadding
  • ZeroPadding
  • Pkcs7
  • Iso10126
  • AnsiX923
  • Iso7816

尝试各种模式

各种填充模式尝试,发现都解密不出来。

  • 提示:由于Java加密后产生的bytes[]转换为了十六进制字符串。所以我多列出来了Rust的对应方法。Java byte是有符号数字,Rust是无符号数字u8,不知道是否有影响。
  • 我的aes+ecb代码:

Cargo.toml

ecb = "0.1.2"
aes = "0.8.4"


use base64::prelude::BASE64_STANDARD_NO_PAD;
use base64::Engine as _;

use aes::cipher::{block_padding::{AnsiX923, Iso10126, Iso7816, NoPadding, Pkcs7, ZeroPadding}, BlockDecryptMut, BlockEncryptMut, KeyInit};

type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;
type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;

/// 十六进制转成u8 array,Java解密也是先讲hex转换为bytes
fn hex_to_u8_array(hex_str: &str, len: usize) -> Result<Vec<u8>, String> {
    if hex_str.len() != len * 2 {
        return Err("Invalid length".to_string());
    }

    // println!("hex_str: {}", hex_str);
    let mut result = Vec::new();
    for (i, chunk) in hex_str.chars().collect::<Vec<char>>().chunks(2).enumerate() {
        // println!("i={i} chunk: {:?}", chunk);
        let byte = u8::from_str_radix(&chunk.iter().collect::<String>(), 16)
            .map_err(|_| "Invalid hexadecimal value".to_string())?;
        result.push(byte);
    }

    Ok(result)
}

/// u8 array转成十六进制,Java加密后将bytes array转换为了hex
fn u8_array_to_hex(arr: &[u8]) -> String {
    arr.iter()
        .map(|&byte| format!("{:02X}", byte))
        .collect::<String>()
}

/// key 的len都是16, 执行后,data已变为加密内容
fn aes_ecb_encrypt(key: &str, data: String) -> String {
    let mut secret = [0u8; 16];
    secret[..key.len()].copy_from_slice(&key.as_bytes());

    let data = data.as_bytes();

    // 目前data长度不会超过32
    let mut buf = [0u8; 32];
    let pt_len = data.len();
    buf[..pt_len].copy_from_slice(&data);

    let encrypt = Aes128EcbEnc::new(&secret.into())
        .encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
        // .encrypt_padded_mut::<NoPadding>(&mut buf, pt_len)
        .map_err(|e| println!("encrypt err : {}", e))
        .unwrap();

    println!("{:?}", encrypt);

    let hex_str = u8_array_to_hex(encrypt);

    println!("hex_str: {hex_str}");

    hex_str
}

/// key 的len都是16,执行后,data已变为解密内容
fn aes_ecb_decrypt(key: &str, data: String) -> String {
    if data.len() < 1 {
        return "".to_string();
    }

    let mut secret = [0u8; 16];
    secret[..key.len()].copy_from_slice(key.as_bytes());

    let mut en_data = match hex_to_u8_array(&data, data.len() / 2) {
        Ok(bytes) => bytes,
        Err(e) => {
            println!("data is err : {}, because : {}", data, e);
            return "".to_string();
        }
    };

    println!("{:?}", en_data);

    // let size = (en_data.len() + 15) / 16;

    let decrypt_padded_mut = Aes128EcbDec::new(&secret.into())
        .decrypt_padded_mut::<Pkcs7>(&mut en_data);
        // .decrypt_padded_mut::<NoPadding>(&mut en_data);

    let decrypt = decrypt_padded_mut
        .map_err(|e| println!("decrypt err : {}", e))
        .unwrap();

    String::from_utf8_lossy(decrypt).to_string()
}

mod test {
    use super::{hex_to_u8_array, u8_array_to_hex};

    #[test]
    fn test_decrypt() {
        // 临时起来个加密key
        let key = "6661428";
        // java用上key加密后的bytes array转换为十六进制字符串结果
        let code = "02C282402EF9671561D56F9693AD6FBDAFA0AC62497EC19E1C6470E177142D48".to_string();
        let text: String = crate::utils::crypto::aes_ecb_decrypt(key, code);
        println!("text: {}", text);
        // 明文
        // println!("expect: 1726104884_1428_c816bd50266a");
        assert_eq!(text, "1726104884_1428_c816bd50266a");
    }

    #[test]
    fn test_aes() {
        let key = "6661428";
        let data = "1726104884_1428_c816bd50266a".to_string();

        println!("raw: {:?}", data);

        let code = crate::utils::crypto::aes_ecb_encrypt(key, data);

        println!("output: {}", code);
        println!("expect: 02C282402EF9671561D56F9693AD6FBDAFA0AC62497EC19E1C6470E177142D48");

        let text = crate::utils::crypto::aes_ecb_decrypt(key, code);
        println!("text: {}", text);
        println!("expect: 1726104884_1428_c816bd50266a");
    }


}

寻求高能大神帮忙看看Rust采用AES128+ECB+PKCS5Padding如何实现。

评论区

写评论
作者 ppluck 2024-09-25 18:02

结贴,已经搞定,具体如下


  • sha1prng原理:
  1. 将SHA-1算法和PRNG算法结合,生成伪随机数,步骤:
    • 计算熵值:熵值是随机性的度量,取自系统时间、内存使用情况等信息。
    • 将计算出来的熵值做为SHA-1算法秘钥,再加上一个计数器作为消息,生成hash值
    • 使用梅森旋转算法生成伪随机数。梅森旋转算法需要一个初始向量,将hash值作为初始向量,通过迭代来生成一序列随机数。SHA1PRNG算法每生成一个随机数,更新一次hash值
    • 初始化计数器,计数器用于防止攻击者通过短时间内的暴力攻击得到相同的随机数。SHA1PRNG算法会记录生成的随机数的计数器值,每次重新初始化时,计数器值夜一并重新初始化。

// Java 代码,key是32位 hex string

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(pwdKey.getBytes());

//对应js处理

CryptoJS.SHA1(CryptoJS.SHA1(key)).toString().substring(0, 32)
CryptoJS.enc.Hex.parse(hex-string)

基于此,rust对应的写法(已验证可行):

Cargo.toml sha1 = "0.10.6"


use sha1::{Digest, Sha1};

fn sha1prng(key: Vec<u8>) -> Vec<u8>  {
    
    // 第一次 SHA-1 哈希
    let mut hasher = Sha1::default();
    hasher.update(key);
    let first_hash = hasher.finalize();

    // 第二次 SHA-1 哈希
    let mut hasher = Sha1::default();
    hasher.update(first_hash);
    let second_hash = hasher.finalize();

    // 转换为十六进制字符串,并取前 32 字节
    // hex::encode_upper(&second_hash[..]).chars().take(32).collect()

    // 未转换,则取前16字节
    second_hash[0..16].into()
}

最终对应java的加解密 rust实现全码:

Cargo.toml

md5 = "0.7.0"
aes = "0.8.4"
hex-literal = "0.4.1"
ecb = "0.1.2"
hex = "0.4.3"
# rand_chacha = "0.3.1"
rand_aes = "0.3.1"
sha1 = "0.10.6"

完整代码如下:




use aes::cipher::{block_padding::Pkcs7, generic_array::GenericArray, BlockDecryptMut, BlockEncryptMut, KeyInit};
use log::error;
use sha1::{Digest, Sha1};

type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;
type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;

pub(crate) fn md5_hex(msg: &str) -> String {
    let digest = md5::compute(msg);
    // :x 转为 十六进制字符串
    format!("{:x}", digest)
}


/// key 的len都是16, 执行后,data已变为加密内容
fn aes_ecb_encrypt(key: Vec<u8>, data: String) -> String {

    let key = sha1prng(key);
    let secret = GenericArray::from_slice(&key); 

    let data = data.as_bytes();

    // 目前data长度不会超过32
    let mut buf = [0u8; 32];
    let pt_len = data.len();
    buf[..pt_len].copy_from_slice(&data);

    let encrypt = Aes128EcbEnc::new(secret)
        .encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
        .map_err(|e| error!("encrypt err : {}", e))
        .unwrap();

    // println!("{:?}", encrypt);

    let hex_str = hex::encode_upper(encrypt);

    // println!("hex_str: {hex_str}");

    hex_str
}

/// key 的len都是16,执行后,data已变为解密内容
fn aes_ecb_decrypt(key: Vec<u8>, data: String) -> String {
    if data.len() < 1 {
        return "".to_string();
    }

    let key = sha1prng(key);
    let secret = GenericArray::from_slice(&key); 

    let mut en_data = match hex::decode(&data) {
        Ok(bytes) => bytes,
        Err(e) => {
            error!("data is err : {}, because : {}", data, e);
            return "".to_string();
        }
    };

    // println!("{:?}", en_data);

    let decrypt_padded_mut = Aes128EcbDec::new(secret)
        .decrypt_padded_mut::<Pkcs7>(&mut en_data);

    let decrypt = decrypt_padded_mut
        .map_err(|e| println!("decrypt err : {}", e))
        .unwrap();

    String::from_utf8_lossy(decrypt).to_string()
}


/// sha1prng原理:
/// 1. 将SHA-1算法和PRNG算法结合,生成伪随机数,步骤:
/// * 计算熵值:熵值是随机性的度量,取自系统时间、内存使用情况等信息。
/// * 将计算出来的熵值做为SHA-1算法秘钥,再加上一个计数器作为消息,生成hash值
/// * 使用梅森旋转算法生成伪随机数。梅森旋转算法需要一个初始向量,将hash值作为初始向量,通过迭代来生成一序列随机数。SHA1PRNG算法每生成一个随机数,更新一次hash值
/// * 初始化计数器,计数器用于防止攻击者通过短时间内的暴力攻击得到相同的随机数。SHA1PRNG算法会记录生成的随机数的计数器值,每次重新初始化时,计数器值夜一并重新初始化。
/// 
/// 此函数相当于Java的
/// ```java
/// 
/// SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
/// random.setSeed(key.getBytes());
/// 
/// KeyGenerator kgen = KeyGenerator.getInstance("AES");
/// kgen.init(128, random);
/// SecretKey secretKey = kgen.generateKey();
/// byte[] enCodeFormat = secretKey.getEncoded();
/// 
/// System.out.println("enCodeFormat : " + Arrays.toString(enCodeFormat)+" hex: " + HexFormat.of().formatHex(enCodeFormat));
/// ```
fn sha1prng(key: Vec<u8>) -> Vec<u8>  {
    
    // 第一次 SHA-1 哈希
    let mut hasher = Sha1::default();
    hasher.update(key);
    let first_hash = hasher.finalize();

    // 第二次 SHA-1 哈希
    let mut hasher = Sha1::default();
    hasher.update(first_hash);
    let second_hash = hasher.finalize();

    // 转换为十六进制字符串,并取前 32 字节
    // hex::encode_upper(&second_hash[..]).chars().take(32).collect()

    // 未转换,则取前16字节
    second_hash[0..16].into()
}

mod test {
    use hex::encode_upper;
    use hex_literal::hex;


    #[test]
    fn test_rand() {
        
        let key = "6661428";
        
        let hash = crate::utils::crypto::sha1prng(key.as_bytes().to_vec());
        println!("hash : {:?} ", hash.clone());
        println!("hash hex : {:?} ", encode_upper(hash.clone()));

        let expect = hex!("E343A4A2F36F5315015A067F493071A8");

        assert_eq!(hash, expect);

    }

    #[test]
    fn test_decrypt() {
        let key = "6661428".as_bytes().to_vec();
        let code = "02C282402EF9671561D56F9693AD6FBDAFA0AC62497EC19E1C6470E177142D48".to_string();
        let text: String = crate::utils::crypto::aes_ecb_decrypt(key, code);
        println!("text: {}", text);
        println!("expect: 1726104884_1428_c816bd50266a");
        assert_eq!(text, "1726104884_1428_c816bd50266a");
    }

    #[test]
    fn test_aes() {
        let key = "6661428".as_bytes().to_vec();
        let data = "1726104884_1428_c816bd50266a".to_string();

        println!("raw: {:?}", data);

        let code: String = crate::utils::crypto::aes_ecb_encrypt(key.clone(), data.clone());
        let expect = "02C282402EF9671561D56F9693AD6FBDAFA0AC62497EC19E1C6470E177142D48";

        println!("output: {}", code);
        println!("40的结果, expect: {}", expect);

        assert_eq!(code, expect);
        
        let text: String = crate::utils::crypto::aes_ecb_decrypt(key, code);
        println!("text: {}", text);
        println!("expect: {data}");
        
        assert_eq!(text, data);

    }


    #[test]
    fn test_md5_hex() {
        let mac = "c8:16:bd:50:26:6a";
        let uuid = "20160812200513000mxZJgJEe11425";
        let msg = format!("{}{}", mac, uuid);

        let md5_api_key = crate::utils::crypto::md5_hex(&msg);

        assert_eq!(md5_api_key, "b733342c47986b9e3712e9792e9a3b63");
    }

    #[test]
    fn test_base64() {
        let code = "YTY2MjA1ZGI2MThjLzIwMTYwODEyMjAwNTEzMDAwbXhaSmdKRWUxMTQyNS8yLjEuMC42ODAxOQ";

        let text = match base64::Engine::decode(&base64::prelude::BASE64_STANDARD_NO_PAD, code) {
            Ok(bytes) => String::from_utf8(bytes).unwrap_or_default(),
            Err(e) => {
                println!("decode_key error: {:?} for {}", e, code);
                "empty".to_string()
            }
        };

        println!("{text}");
        assert_eq!(
            "a66205db618c/20160812200513000mxZJgJEe11425/2.1.0.68019",
            text
        );

        let code2 = base64::Engine::encode(&base64::prelude::BASE64_STANDARD_NO_PAD, text);
        println!("{code2}");

        assert_eq!(code, code2);
    }
}

作者 ppluck 2024-09-20 09:36

继续尝试了rand_aes库,仍然没与java一致。看来得找java的实现方法重新做一个库了。

Cargo.toml

rand_aes = "0.3.1"

    #[test]
    fn test_rand() {
        
        let key = "6661428";
        let mut key_bytes = [0u8; 16];
        key_bytes[..key.len()].copy_from_slice(&key.as_bytes());
        let nonce = [0u8; 8];
        let counter = 0;

        let mut prng = Aes128Ctr64::from_seed(Aes128Ctr64Seed::new(key_bytes, nonce, counter));
        
        let mut seckey = [0u8; 16]; 
        prng.fill(&mut seckey);

        println!("seckey: {:?} hex: {:?}", seckey, hex::encode_upper(seckey));
    }

输出:

seckey: [249, 28, 201, 92, 173, 195, 46, 192, 188, 187, 245, 60, 238, 22, 223, 205] hex: "F91CC95CADC32EC0BCBBF53CEE16DFCD"

    #[test]
    fn test_rand() {
        
        let key = "6661428";
        let mut key_bytes = [0u8; 16];
        key_bytes[..key.len()].copy_from_slice(&key.as_bytes());
        let counter = 0;

        let mut prng = Aes128Ctr128::from_seed(Aes128Ctr128Seed::new(key_bytes, counter));
        
        let mut seckey = [0u8; 16]; 
        prng.fill(&mut seckey);

        println!("seckey: {:?} hex: {:?}", seckey, hex::encode_upper(seckey));
    }

输出:

seckey: [249, 28, 201, 92, 173, 195, 46, 192, 188, 187, 245, 60, 238, 22, 223, 205] hex: "F91CC95CADC32EC0BCBBF53CEE16DFCD"

--
👇
ppluck:

--
👇
TinusgragLin: > 即:找到java.security.SecureRandom在Rust中基于种子的同样算法实现即可

一定要同样的算法吗?只是(密码学安全地)随机产生 key 的话,感觉用 rand 库就好了,它的 thread_rng() 用的 StdRng 就是密码学安全的。

我在 github 上搜了一下 sha1 prng/sha1 rng/sha1 random,Rust 语言这边都没找到什么结果,要自己移植 Java 那边 SHA1PRNG 的实现的话应该要花不少时间的。

-- 是的,否则无法兼容生产上的流量,会解密失败。

用rand和rand_chacha

Cargo.toml

rand_chacha = "0.3.1"
rand = {version = "0.8.5", features = ["std"]}
        let key = "6661428";
        let mut key_bytes = [0u8; 32];
        key_bytes[..key.len()].copy_from_slice(&key.as_bytes());

        // let mut seed = ChaCha8Rng::from_seed(key_bytes);
        let mut seed = ChaCha12Rng::from_seed(key_bytes);
        let mut seckey = [0u8; 16]; 
        seed.fill(&mut seckey);

        println!("seckey: {:?} hex: {:?}", seckey, hex::encode_upper(seckey));

结果:

seckey: [185, 231, 201, 161, 57, 20, 227, 186, 148, 173, 65, 160, 43, 224, 136, 239] hex: "B9E7C9A13914E3BA94AD41A02BE088EF"

都与Java的不一致:

        String key = "6661428";
	SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
	random.setSeed(key.getBytes());
			
	KeyGenerator kgen = KeyGenerator.getInstance("AES");
	kgen.init(128, random);
	SecretKey secretKey = kgen.generateKey();
	byte[] enCodeFormat = secretKey.getEncoded();
			
	System.out.println("enCodeFormat : " + Arrays.toString(enCodeFormat)+" hex: " + HexFormat.of().formatHex(enCodeFormat));


结果:

enCodeFormat : [-29, 67, -92, -94, -13, 111, 83, 21, 1, 90, 6, 127, 73, 48, 113, -88] hex: e343a4a2f36f5315015a067f493071a8
作者 ppluck 2024-09-20 08:57

--
👇
TinusgragLin: > 即:找到java.security.SecureRandom在Rust中基于种子的同样算法实现即可

一定要同样的算法吗?只是(密码学安全地)随机产生 key 的话,感觉用 rand 库就好了,它的 thread_rng() 用的 StdRng 就是密码学安全的。

我在 github 上搜了一下 sha1 prng/sha1 rng/sha1 random,Rust 语言这边都没找到什么结果,要自己移植 Java 那边 SHA1PRNG 的实现的话应该要花不少时间的。

-- 是的,否则无法兼容生产上的流量,会解密失败。

用rand和rand_chacha

Cargo.toml

rand_chacha = "0.3.1"
rand = {version = "0.8.5", features = ["std"]}
        let key = "6661428";
        let mut key_bytes = [0u8; 32];
        key_bytes[..key.len()].copy_from_slice(&key.as_bytes());

        // let mut seed = ChaCha8Rng::from_seed(key_bytes);
        let mut seed = ChaCha12Rng::from_seed(key_bytes);
        let mut seckey = [0u8; 16]; 
        seed.fill(&mut seckey);

        println!("seckey: {:?} hex: {:?}", seckey, hex::encode_upper(seckey));

结果:

seckey: [185, 231, 201, 161, 57, 20, 227, 186, 148, 173, 65, 160, 43, 224, 136, 239] hex: "B9E7C9A13914E3BA94AD41A02BE088EF"

都与Java的不一致:

        String key = "6661428";
	SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
	random.setSeed(key.getBytes());
			
	KeyGenerator kgen = KeyGenerator.getInstance("AES");
	kgen.init(128, random);
	SecretKey secretKey = kgen.generateKey();
	byte[] enCodeFormat = secretKey.getEncoded();
			
	System.out.println("enCodeFormat : " + Arrays.toString(enCodeFormat)+" hex: " + HexFormat.of().formatHex(enCodeFormat));


结果:

enCodeFormat : [-29, 67, -92, -94, -13, 111, 83, 21, 1, 90, 6, 127, 73, 48, 113, -88] hex: e343a4a2f36f5315015a067f493071a8
TinusgragLin 2024-09-19 19:26

即:找到java.security.SecureRandom在Rust中基于种子的同样算法实现即可

一定要同样的算法吗?只是(密码学安全地)随机产生 key 的话,感觉用 rand 库就好了,它的 thread_rng() 用的 StdRng 就是密码学安全的。

我在 github 上搜了一下 sha1 prng/sha1 rng/sha1 random,Rust 语言这边都没找到什么结果,要自己移植 Java 那边 SHA1PRNG 的实现的话应该要花不少时间的。

作者 ppluck 2024-09-19 18:20

即:找到java.security.SecureRandom在Rust中基于种子的同样算法实现即可

作者 ppluck 2024-09-19 11:53

非常感谢,你这个方法没问题。再你的这个方法提示下,我找到了问题根源,但是没找到rust里aes+ecb如何代替。

我的旧的程序Java生成key的方式是这样的,我继续再找找看:


SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());

KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, random);
SecretKey secretKey = kgen.generateKey();
			

只所以不采用你列的这种方式,是因为我要兼容生产已经采用的上面方式生成Key:

"6661428\0\0\0\0\0\0\0\0\0".getBytes()

这样就需要在rust里找到一种与java上面生成key一致的方法库

--
👇
TinusgragLin: 我这边测试了一下加密,结果一致,都是 b15f0b20af79086a71ea2f59e96636d75759573616d6de356b9ee6708bf2f18e:

use aes::cipher::{block_padding::Pkcs7, BlockEncryptMut, KeyInit};
use hex::encode as to_hex;
type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;

fn aes_ecb_encrypt(key: &str, data: &str) -> String {
    let mut secret = [0u8; 16];
    secret[..key.len()].copy_from_slice(&key.as_bytes());

    let mut buf = [0u8; 32];
    let pt_len = data.len();
    buf[..pt_len].copy_from_slice(data.as_bytes());

    let encrypt = Aes128EcbEnc::new(&secret.into())
        .encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
        .expect("Oh no!");

    let hex_str = to_hex(encrypt);

    hex_str
}

fn main() {
    let key = "6661428";
    let data = "1726104884_1428_c816bd50266a";
    println!("{}", aes_ecb_encrypt(key, data));
}
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;

public class Main {
    public static byte[] encryptMessage(byte[] message, byte[] keyBytes)
         throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException
    {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(message);
    }

    public static void main(String[] args) {
        try {
            byte[] bytes = encryptMessage("1726104884_1428_c816bd50266a".getBytes(), "6661428\0\0\0\0\0\0\0\0\0".getBytes());
            System.out.println(HexFormat.of().formatHex(bytes));
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
swordlet 2024-09-15 23:51

aes192, cbc, pkcs5, 仅供参考:

use aes::cipher::{block_padding::NoPadding, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
type Aes192CbcEnc = cbc::Encryptor<aes::Aes192>;
type Aes192CbcDec = cbc::Decryptor<aes::Aes192>;

const AES_BLOCK_SIZE: usize = 16;

pub fn aes_cbc_decrypt(ciphertext: &[u8], key: [u8; 24], iv: [u8; 16]) -> Vec<u8> {
    let mut buf = ciphertext.to_owned();

    let pt = Aes192CbcDec::new(&key.into(), &iv.into())
        .decrypt_padded_mut::<NoPadding>(&mut buf)
        .unwrap();
    let pad_trim = pkcs5_trim(pt);
    pad_trim.to_vec()
}

pub fn aes_cbc_encrypt(plaintext: &[u8], key: [u8; 24], iv: [u8; 16]) -> Vec<u8> {
    let mut buf = pkcs5_pad(plaintext, AES_BLOCK_SIZE);
    let pt_len = buf.len();

    let ct = Aes192CbcEnc::new(&key.into(), &iv.into())
        .encrypt_padded_mut::<NoPadding>(&mut buf, pt_len)
        .unwrap();

    ct.to_vec()
}

fn pkcs5_pad(data: &[u8], block_size: usize) -> Vec<u8> {
    let pad_len = block_size - data.len() % block_size;
    let mut padded_data = data.to_vec();
    padded_data.extend(std::iter::repeat(pad_len as u8).take(pad_len));
    padded_data
}

fn pkcs5_trim(data: &[u8]) -> &[u8] {
    let pad_len = data[data.len() - 1] as usize;
    &data[..data.len() - pad_len]
}
TinusgragLin 2024-09-14 19:58

我这边测试了一下加密,结果一致,都是 b15f0b20af79086a71ea2f59e96636d75759573616d6de356b9ee6708bf2f18e:

use aes::cipher::{block_padding::Pkcs7, BlockEncryptMut, KeyInit};
use hex::encode as to_hex;
type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;

fn aes_ecb_encrypt(key: &str, data: &str) -> String {
    let mut secret = [0u8; 16];
    secret[..key.len()].copy_from_slice(&key.as_bytes());

    let mut buf = [0u8; 32];
    let pt_len = data.len();
    buf[..pt_len].copy_from_slice(data.as_bytes());

    let encrypt = Aes128EcbEnc::new(&secret.into())
        .encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
        .expect("Oh no!");

    let hex_str = to_hex(encrypt);

    hex_str
}

fn main() {
    let key = "6661428";
    let data = "1726104884_1428_c816bd50266a";
    println!("{}", aes_ecb_encrypt(key, data));
}
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;

public class Main {
    public static byte[] encryptMessage(byte[] message, byte[] keyBytes)
         throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException
    {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(message);
    }

    public static void main(String[] args) {
        try {
            byte[] bytes = encryptMessage("1726104884_1428_c816bd50266a".getBytes(), "6661428\0\0\0\0\0\0\0\0\0".getBytes());
            System.out.println(HexFormat.of().formatHex(bytes));
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
asuper 2024-09-14 17:22

加解密和JAVA都不熟,提一点小的看法。 你对比HAVA和RUST的时候,先不要去解密,你只做加密,看看两边结果是否能一致。另外十六进制字符串应该是无所谓有没有符号的。

1 共 9 条评论, 1 页