# 敏感数据处理

# 校验数据合法性

当开发者通过getUserInfo接口请求到敏感数据时,返回的signature字段是在小程序服务器端通过如下算法得到:

signature = sha1(`${rawData}${base64(session_key)}`)
1

开发者可以在自己的服务器端执行同样的算法,来校验数据是否合法。

# 解密敏感数据

  1. 对称解密使用的算法为 AES-128-CBC,数据采用 PKCS#5 填充。
  2. 对称解密的目标密文为 encryptedData
  3. 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 长度为16Byte。
  4. 对称解密算法初始向量为 Base64_Decode(iv)

# 代码示例

代码示例中的 sessionKey 为会话密钥, 由 code2session 返回。

//nodejs语言
const crypto = require("crypto");

sessionKey = '36f764e22be7fc5edb07f56073a4b770';
iv = 'f210af090c830cd5c47e67eeb4f52001';
message = 'Ssy006/cEV4+a2wUWTGvHuXIkU+vX1KCNUkLntKKVXzggx6yfycO/RDkx55A8TvhI5EUGKJ8X4x3NBCypWlkvODaCSDw6uUBhqbDaRS9vH57Dk9nVcw+u2ANGU4PmW/WX2ihze8hDMpofD/pDfZCkEjnvmqkeZ+jPxq351bpDwTUJ+mae92iL9DuHd+xz6WC5cUx9HE8AbxUD18dGUwt2KcNNi9ePvgcvGZBEgZGG2s=';
function decrypt_with_aes() {
    message = Buffer.from(message, 'base64');
    const decipher = crypto.createDecipheriv(
        'aes-128-cbc',
        Buffer.from(sessionKey, 'hex'),
        Buffer.from(iv, 'hex'),
    );
    let decrypted = decipher.update(message);
    decrypted += decipher.final();
    const data = decrypted.toString();
    const json = JSON.parse(data);
    return json;
}
console.log(decrypt_with_aes())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#python3语言
import base64
import json
from Crypto.Cipher import AES

def decrypt_with_aes():
    session_key = '36f764e22be7fc5edb07f56073a4b770';
    iv = "f210af090c830cd5c47e67eeb4f52001";
    message = "Ssy006/cEV4+a2wUWTGvHuXIkU+vX1KCNUkLntKKVXzggx6yfycO/RDkx55A8TvhI5EUGKJ8X4x3NBCypWlkvODaCSDw6uUBhqbDaRS9vH57Dk9nVcw+u2ANGU4PmW/WX2ihze8hDMpofD/pDfZCkEjnvmqkeZ+jPxq351bpDwTUJ+mae92iL9DuHd+xz6WC5cUx9HE8AbxUD18dGUwt2KcNNi9ePvgcvGZBEgZGG2s=";
    session_key = bytes.fromhex(session_key)
    iv = bytes.fromhex(iv)
    message = base64.b64decode(message)
    print(session_key, len(session_key), type(session_key))
    print(len(message))
    decipher = AES.new(session_key, AES.MODE_CBC, iv)
    decodedMessage = decipher.decrypt(message)
    unpad = lambda s: s[0:-s[-1]]
    decodedMessage = unpad(decodedMessage)
    data = json.loads(decodedMessage)
    return data
print(decrypt_with_aes())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//java语言    
import org.junit.jupiter.api.Test;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;


public class DecryptTest {
    @Test
    public void test_decrypt() throws Exception {
        System.out.println(decrypt_with_aes());
    }

    public static String decrypt_with_aes() throws Exception {

        String session_key = "36f764e22be7fc5edb07f56073a4b770";
        String iv = "f210af090c830cd5c47e67eeb4f52001";
        String message = "Ssy006/cEV4+a2wUWTGvHuXIkU+vX1KCNUkLntKKVXzggx6yfycO/RDkx55A8TvhI5EUGKJ8X4x3NBCypWlkvODaCSDw6uUBhqbDaRS9vH57Dk9nVcw+u2ANGU4PmW/WX2ihze8hDMpofD/pDfZCkEjnvmqkeZ+jPxq351bpDwTUJ+mae92iL9DuHd+xz6WC5cUx9HE8AbxUD18dGUwt2KcNNi9ePvgcvGZBEgZGG2s=";

        Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hexToBytes(session_key), "AES"), new IvParameterSpec(hexToBytes(iv)));

        byte[] r = cipher.doFinal(Base64.getDecoder().decode(message));

        byte lastByte = r[r.length - 1];
        if (lastByte <= 16) {   // if has PKCS7Padding padding, do unpadding
            byte[] rr = new byte[r.length - lastByte];
            System.arraycopy(r, 0, rr, 0, rr.length);
            r = rr;
        }

        return new String(r);
    }

    private static byte[] hexToBytes(String s) {
        s = s.toLowerCase();
        String hexVal = "0123456789abcdef";
        byte[] out = new byte[s.length() / 2];

        int n = s.length();
        for (int i = 0; i < n; i += 2) {
            int hn = hexVal.indexOf(s.charAt(i));
            int ln = hexVal.indexOf(s.charAt(i + 1));
            out[i / 2] = (byte) ((hn << 4) | ln);
        }
        return out;
    }
}
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
// golang语言
import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"testing"
)

func decrypt_with_aes(message_ string, sessionKey_ string, iv_ string) (string, error) {
	sessionKey, err := hex.DecodeString(sessionKey_)
	if err != nil {
		return "", err
	}

	iv, err := hex.DecodeString(iv_)
	if err != nil {
		return "", err
	}

	message, err := base64.StdEncoding.DecodeString(message_)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(sessionKey)
	if err != nil {
		return "", err
	}

	res := make([]byte, len(message))
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(res, message)

	unpaddingLen := 0
	if p := res[len(res)-1]; p <= 16 { // if has PKCS7Padding padding, do unpadding
		unpaddingLen = int(p)
	}

	res = res[0 : len(res)-unpaddingLen]
	return string(res), nil
}

func TestAES(t *testing.T) {
	session_key := "36f764e22be7fc5edb07f56073a4b770"
	iv := "f210af090c830cd5c47e67eeb4f52001"
	message := "Ssy006/cEV4+a2wUWTGvHuXIkU+vX1KCNUkLntKKVXzggx6yfycO/RDkx55A8TvhI5EUGKJ8X4x3NBCypWlkvODaCSDw6uUBhqbDaRS9vH57Dk9nVcw+u2ANGU4PmW/WX2ihze8hDMpofD/pDfZCkEjnvmqkeZ+jPxq351bpDwTUJ+mae92iL9DuHd+xz6WC5cUx9HE8AbxUD18dGUwt2KcNNi9ePvgcvGZBEgZGG2s="

	fmt.Println(decrypt_with_aes(message, session_key, iv))
}
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
最后更新于 : 1/4/2023, 2:39:31 PM