☻Blog("Laziji")

System.out.print("辣子鸡的博客");

python版本3.6, 在windows以及linux上都进行过测试

由于pythonrsa加密存在长度限制, 虽然可以通过分片加密来解决,
但是更好的做法是通过rsa加密传输aes密钥给服务器, 携带的信息通过该密钥进行aes加密,
服务器通过rsa私钥得到aes密钥后解析信息, 并继续使用密钥进行双向通信

python中加密使用pycryptodome模块

1
pip install pycryptodome

Python

其中接受的参数text,key均为字符串

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
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Cipher import AES
from base64 import b64decode
from base64 import b64encode
import re


def rsa_encode(text, public_key):
key = RSA.importKey(b64decode(public_key))
cipher = PKCS1_v1_5.new(key)
return b64encode(cipher.encrypt(text.encode(encoding='utf-8'))).decode('utf-8')


def aes_encode(text, key):
key = key.encode('utf-8')
cipher = AES.new(key, AES.MODE_CBC, key)
text = text + (16 - len(text) % 16) * chr(16 - len(text) % 16)
return b64encode(cipher.encrypt(text.encode(encoding='utf-8'))).decode('utf-8')


def aes_decode(cipher_text, key):
key = key.encode('utf-8')
cipher = AES.new(key, AES.MODE_CBC, key)
text = cipher.decrypt(b64decode(cipher_text)).decode('utf-8')
return re.compile('[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f\n\r\t]').sub('', text)

Java

java的加密代码不依赖其他包

AesUtils

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
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.UUID;

public class AesUtils {

private static final String TYPE = "AES";
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String CHARSET = "UTF-8";

public static String createKey(){
return UUID.randomUUID().toString().replace("-","").substring(16);
}

public static String encrypt(String data, String key) throws Exception {
IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), TYPE);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(CHARSET)));
}

public static String decrypt(String data, String key) throws Exception {
IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), TYPE);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(data.getBytes())), CHARSET);
}
}

RsaUtils

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
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtils {

private static final String TYPE = "RSA";
private static final String ALGORITHM = "RSA/ECB/PKCS1PADDING";
private static final String CHARSET = "UTF-8";
private static final int KEY_SIZE = 1024;

public static KeyPair createKeyPair() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(TYPE);
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
} catch (Exception e) {
return null;
}
}

public static String getPublicKey(KeyPair keyPair) {
return Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
}

public static String getPrivateKey(KeyPair keyPair) {
return Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
}

public static String encrypt(String data, String publicKeyString) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
KeyFactory keyFactory = KeyFactory.getInstance(TYPE);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString));
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.getEncoder().encodeToString(splitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET)));
}

public static String decrypt(String data, String privateKeyString) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
KeyFactory keyFactory = KeyFactory.getInstance(TYPE);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString));
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(splitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getDecoder().decode(data)), CHARSET);
}

private static byte[] splitCodec(Cipher cipher, int mode, byte[] data) throws Exception {
int maxBlock = KEY_SIZE / 8 - (mode == Cipher.DECRYPT_MODE ? 0 : 11);
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer;
for (int offset = 0; offset < data.length; offset += maxBlock) {
buffer = cipher.doFinal(data, offset, Math.min(maxBlock, data.length - offset));
out.write(buffer, 0, buffer.length);
}
return out.toByteArray();
}
}
}

在JS中对于普通的json, 可用如下方式进行简单的深度拷贝

1
2
let json = { a: "aa" };
let newJson = JSON.parse(JSON.stringify(json));

不过当json中包含一些JS中的对象及函数的时候, 用这样的方法会使数据丢失, 并且这个无法解决循环引用的问题, 所谓循环引用指的是

1
2
3
4
5
6
7
let b={};
let a={
b:b
};
b.a=a;
console.log(a);
// console.log(JSON.stringify(a));

这时JSON.stringify(a)就出现了异常

由于存在这些问题, 所以就编写了一个拷贝函数, 来做这件事情, 代码实现如下

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function copyObject(o) {
let objectMap = new WeakMap();
function copy(object) {
if (objectMap.get(object)) {
return objectMap.get(object);
}
if (typeof object !== "object") {
return object;
}
if (object instanceof RegExp) {
return object;
}
let newObject = new object.constructor();
objectMap.set(object, newObject);
for (let k in object) {
newObject[k] = copy(object[k]);
}
return newObject;
}
return copy(o);
}

代码中通过let objectMap = new WeakMap();保存拷贝过的对象, 解决循环引用的问题

通过递归拷贝其中的对象, 若是基本类型、正则对象或函数则直接返回

测试代码

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
class ObjA {
constructor(v) {
this.a = v;
}
print() {
console.log(this.a || "!!!");
}
}
function ObjB(v) {
this.name
let a = v;
this.print = () => {
console.log(a || "!!!");
}
}
let objA = new ObjA(666);
let objB = new ObjB(777);
let json0 = {};
let json1 = {
a: () => 'aaa',
b: [123, "abc", /abcd/],
c: {
d: function () { return "ddd" },
e: [123, 2, 3],
f: objA,
g: objB
},
r: json0
}
json0.r = json1;

let json2 = copyObject(json1);
json2.c.e[1] = "asdasd";
json2.r.r.r.r.b[1] = "rrrr";
console.log(json1);
console.log(json2);

json2.c.f.print();
objA.a = 888;
objA.print();
json2.c.f.print();
json2.c.g.print();

经过测试, 以上场景的输出均与预计相同

0%