Rust 通过 JNI 提供加密解密接口

Posted by koocyton on 2023-01-08
Estimated Reading Time 6 Minutes
Words 1.1k In Total
Viewed Times

目标

在配置 Java 项目时,配制参数通过 AES 来加密,但反编译 class,很容易拿到秘钥,
Rust 开发 JNI 提供给 java 接口,是一个相对安全的方法(不是绝对安全)。

配置 Cargo.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[package]
name = "jniutil"
version = "0.2.1"
authors = ["koocyton <koocyton@gmail.com>"]
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
jni = "0.19.0"
rust-crypto = "0.2"
clap = "2.33"
hex = "0.4"
base64 = "0.13.0"
# reqwest = { version = "0.11", features = ["json"] }
# tokio = { version = "1.0", features = ["full"] }
# futures = { version = "0.3.*" }
# async-std = { version = "1", features = ["attributes", "tokio1"] }

[lib]
crate_type = ["cdylib"]

代码 /src/lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// This is the interface to the JVM that we'll
// call the majority of our methods on.
use jni::JNIEnv;

// These objects are what you should use as arguments to your native function.
// They carry extra lifetime information to prevent them escaping this context
// and getting used after being GC'd.
use jni::objects::{JClass, JString};

// This is just a pointer. We'll be returning it from our function.
// We can't return one of the objects with lifetime information because the
// lifetime checker won't let us.
use jni::sys::jstring;

use std::error::Error;
use crypto::aes::KeySize::KeySize128;
use crypto::blockmodes::PkcsPadding;
use crypto::buffer::{RefReadBuffer, RefWriteBuffer};

const DP_CBC_KEY: &str = "1111111111111111";
const DP_CBC_OFFSET: &str = "1111111111111111";

// 注意包名 com_doopp_common_util_JniUtil
#[no_mangle]
pub extern "system" fn Java_com_doopp_common_util_JniUtil_dpEnc(
env: JNIEnv,
// this is the class that owns our
// static method. Not going to be
// used, but still needs to have
// an argument slot
_class: JClass,
input: JString,
) -> jstring {
// First, we have to get the string out of java. Check out the `strings`
// module for more info on how this works.
let input: String = env
.get_string(input)
.expect("Couldn't get java string!")
.into();
// Then we have to create a new java string to return. Again, more info
// in the `strings` module.
let enc_str: String = encrypt(DP_CBC_KEY, DP_CBC_OFFSET, input.as_str()).unwrap();
let output = env
.new_string(enc_str)
.expect("Couldn't get encrypt string!");
// Finally, extract the raw pointer to return.
output.into_inner()
}

// 注意包名 com_doopp_common_util_JniUtil
#[no_mangle]
pub extern "system" fn Java_com_doopp_common_util_JniUtil_dpDec(
env: JNIEnv,
// this is the class that owns our
// static method. Not going to be
// used, but still needs to have
// an argument slot
_class: JClass,
input: JString,
) -> jstring {
// First, we have to get the string out of java. Check out the `strings`
// module for more info on how this works.
let input: String = env
.get_string(input)
.expect("Couldn't get java string!")
.into();
// Then we have to create a new java string to return. Again, more info
// in the `strings` module.
let dec_str: String = decrypt(DP_CBC_KEY, DP_CBC_OFFSET, input.as_str()).unwrap();
let output = env
.new_string(dec_str)
.expect("Couldn't get decrypt string!");
// Finally, extract the raw pointer to return.
output.into_inner()
}

/// 加密
fn encrypt(key: &str, iv: &str, text: &str) -> Result<String, Box<dyn Error>> {
let mut encrypt = crypto::aes::cbc_encryptor(
KeySize128,
key.as_bytes(),
iv.as_bytes(),
PkcsPadding
);
let mut read_buffer = RefReadBuffer::new(text.as_bytes());
let mut result = vec![0; 4096];
let mut write_buffer = RefWriteBuffer::new(&mut result);
encrypt.encrypt(&mut read_buffer, &mut write_buffer, true).unwrap();
let enc_str = base64::encode_config(trim(&result)?, base64::STANDARD);
Ok(enc_str)
}

/// 解密
fn decrypt(key: &str, iv: &str, input: &str) -> Result<String, Box<dyn Error>> {
let mut decrypt = crypto::aes::cbc_decryptor(
KeySize128,
key.as_bytes(),
iv.as_bytes(),
PkcsPadding
);
let base_text = base64::decode_config(input, base64::STANDARD)?;
let mut read_buffer = RefReadBuffer::new(&base_text);
let mut result = vec![0; input.len()];
let mut write_buffer = RefWriteBuffer::new(&mut result);
decrypt.decrypt(&mut read_buffer, &mut write_buffer, true).unwrap();
let dec_str = String::from_utf8(trim(&result)?)?;
Ok(dec_str)
}

fn trim(input: &Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
let zero : u8 = 0;
let mut fz_idx = input.len();
for (idx, val) in input.iter().rev().enumerate() {
if &zero!=val
{
fz_idx = fz_idx - idx;
break;
}
}
Ok(input[0..fz_idx].to_vec())
}

#[cfg(test)]
mod tests {
#[test]
fn it_works() {}
}

Java 调用接口,注意包名和 lib.rs 的一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.doopp.common.util; // 注意包名

import java.io.IOException;

public class JniUtil {

static {
String osName = System.getProperties().getProperty("os.name");
// other linux
String nativeLibrary = "/jni_lib/libjniutil.so";
// win
if (osName.contains("Win")) {
nativeLibrary = "/jni_lib/jniutil.dll";
}
// mac
else if (osName.contains("Mac")) {
nativeLibrary = "/jni_lib/libjniutil.dylib";
}
try {
NativeUtil.loadLibraryFromJar(nativeLibrary);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}

public static native String dpDec(String encText);
}

编译 libjniutil.dylib 并拷贝在 /resources/jni_lib 目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ cargo build
Compiling libc v0.2.126
Compiling memchr v2.5.0
Compiling unicode-width v0.1.9
Compiling bytes v1.2.0
Compiling cfg-if v1.0.0
Compiling cesu8 v1.1.0
Compiling ansi_term v0.12.1
Compiling thiserror v1.0.31
Compiling log v0.4.17
Compiling textwrap v0.11.0
Compiling jni-sys v0.3.0
Compiling rustc-serialize v0.3.24
Compiling bitflags v1.3.2
Compiling vec_map v0.8.2
Compiling strsim v0.8.0
Compiling base64 v0.13.0
Compiling hex v0.4.3
Compiling rand v0.4.6
Compiling atty v0.2.14
Compiling time v0.1.44
Compiling clap v2.34.0
Compiling combine v4.6.4
Compiling rand v0.3.23
Compiling rust-crypto v0.2.36
Compiling jni v0.19.0
Compiling jniutil v0.2.1 (/Users/develop/Project/rust-jni-lib)
Finished dev [unoptimized + debuginfo] target(s) in 17.05s

如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !