temp
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
node_modules
|
||||
|
||||
.env
|
||||
3
packages/decrypt-phone-by-data/.env.example
Normal file
3
packages/decrypt-phone-by-data/.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
APP_ID=68c47321131b9b0001166b7d
|
||||
APP_SECRET=a9f02a4bef2e29b2b6f866d9d2aff2e6
|
||||
CODE=ecd82af4267247b1a1a59b6df7fdd320
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"encryptedData": "NpiIHuKkhY2S8pCG9WShtDcCLmBhnKkU+m0BLUHJKWkUFrkPay6j3Lc7wkzKat8U/gWsP2WfcMHS6nz48VvjyyKAeXrAxQKEFIxLKslRqntEJnvu5HmANssOypHHk7Y7ovij1QvY+Od/pTBKL73i2AzLoNtXubMTWSPcqG0A/Ov1uy4U7zRZpUIa5otQ+o5dqHc4+rj5EnT2MQ73+QGcFA==",
|
||||
"errMsg": "getPhoneNumber:ok",
|
||||
"iv": "aUFhbEhJbWFydjVNQlE4cg=="
|
||||
}
|
||||
6
packages/decrypt-phone-by-data/demo.ts
Normal file
6
packages/decrypt-phone-by-data/demo.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const demoData = {
|
||||
encryptedData:
|
||||
'HdZv6GsUqB68aoLf+SqSaGSqffOTMHHW89DTjh7suCIz9gsdK0wUTpL8+60+zqKcL3b1jNxrRdmZxXGHO67eH1HG5WSoJU7Jlu1WjUWzK9ZhBuPJNMP7Z0aHx9aPOyk1grlZNHOA51/i+AlKywm9QY3RLRTtPQpoSTcjJud1iWhF+aYBrI8pS/rw/AYQuRvOuODjQXinyAC6pJPqZzfs5w==',
|
||||
errMsg: 'getPhoneNumber:ok',
|
||||
iv: 'N2dPdU9wT0k3bnNKZlhsZw==',
|
||||
};
|
||||
@@ -10,5 +10,10 @@
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.14.0",
|
||||
"type": "module"
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.3.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^17.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
62
packages/decrypt-phone-by-data/src/decrypt.ts
Normal file
62
packages/decrypt-phone-by-data/src/decrypt.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
// 解密数据
|
||||
// sessionKey: 微信/小红书登录后获取的 session_key
|
||||
// encryptedData: 小程序通过 wx.getPhoneNumber 获取的 encryptedData
|
||||
// iv: 小程序通过 wx.getPhoneNumber 获取的 iv
|
||||
|
||||
// @links
|
||||
// https://miniapp.xiaohongshu.com/doc/DC591932 // 小红书小程序解密数据
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html // 微信小程序解密数据
|
||||
|
||||
export class BizDataCrypt {
|
||||
private appId: string;
|
||||
private sessionKey: string;
|
||||
|
||||
constructor(appId: string, sessionKey: string) {
|
||||
this.appId = appId;
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
|
||||
decryptData(encryptedData: string, iv: string) {
|
||||
// base64 decode
|
||||
const sessionKeyWA = CryptoJS.enc.Base64.parse(this.sessionKey);
|
||||
const encryptedDataWA = CryptoJS.enc.Base64.parse(encryptedData);
|
||||
const ivWA = CryptoJS.enc.Base64.parse(iv);
|
||||
|
||||
// 确保 AES-128 使用 16 字节 key(微信/小红书 session_key base64 解码应为 16 字节)
|
||||
let keyWA = sessionKeyWA;
|
||||
if (sessionKeyWA.sigBytes !== 16) {
|
||||
if (sessionKeyWA.sigBytes < 16) {
|
||||
throw new Error('Invalid session_key length (<16 bytes)');
|
||||
}
|
||||
// 如果大于 16 字节,截取前 16 字节
|
||||
keyWA = CryptoJS.lib.WordArray.create(sessionKeyWA.words.slice(0, 4), 16);
|
||||
}
|
||||
|
||||
try {
|
||||
// 解密
|
||||
const cipherParams = CryptoJS.lib.CipherParams.create({
|
||||
ciphertext: encryptedDataWA,
|
||||
});
|
||||
const decipher = CryptoJS.AES.decrypt(cipherParams, keyWA, {
|
||||
iv: ivWA,
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
|
||||
const decoded = decipher.toString(CryptoJS.enc.Utf8);
|
||||
if (!decoded) throw new Error('Decryption produced empty result');
|
||||
|
||||
const decodedData = JSON.parse(decoded);
|
||||
const waterAppId = decodedData?.watermark?.appid || decodedData?.watermark?.appId;
|
||||
if (!decodedData || !decodedData.watermark || waterAppId !== this.appId) {
|
||||
throw new Error('Invalid appId in decrypted data');
|
||||
}
|
||||
|
||||
return decodedData;
|
||||
} catch (err: any) {
|
||||
throw new Error('Decrypt failed' + (err?.message ? ': ' + err.message : ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
45
packages/decrypt-phone-by-data/src/get-session-key.ts
Normal file
45
packages/decrypt-phone-by-data/src/get-session-key.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const testBASEURL = 'miniapp-sandbox.xiaohongshu.com';
|
||||
const prodBASEURL = 'miniapp.xiaohongshu.com';
|
||||
const baseURL = 'https://miniapp.xiaohongshu.com/api/rmp/session?app_id=${app_id}&access_token=${access_token}&code=${code}'.replace(prodBASEURL, testBASEURL);
|
||||
const accessTokenURL = 'https://miniapp.xiaohongshu.com/api/rmp/token'.replace(prodBASEURL, testBASEURL);
|
||||
const getAccessToken = async (appId: string, appSecret: string) => {
|
||||
const response = await fetch(accessTokenURL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
appid: appId,
|
||||
secret: appSecret,
|
||||
}),
|
||||
});
|
||||
console.log('Access token response status:', appId, appSecret);
|
||||
const data = await response.json();
|
||||
console.log('Access token response data:', data);
|
||||
if (data.err_no !== 0) {
|
||||
throw new Error(`Error getting access token: ${data.err_msg}`);
|
||||
}
|
||||
console.log('Access token data:', data);
|
||||
return data.access_token;
|
||||
};
|
||||
export const getSessionKey = async (appId: string, accessToken: string, code: string) => {
|
||||
const url = baseURL.replace('${app_id}', appId).replace('${access_token}', accessToken).replace('${code}', code);
|
||||
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
console.log('Session key data:', data);
|
||||
if (data.err_no !== 0) {
|
||||
throw new Error(`Error getting session key: ${data.err_msg}`);
|
||||
}
|
||||
return data.session_key;
|
||||
};
|
||||
|
||||
const appId = process.env.APP_ID || 'your_app_id';
|
||||
const appSecret = process.env.APP_SECRET || 'your_app_secret';
|
||||
const authorization_code = process.env.CODE || 'your_code';
|
||||
// const accessToken = await getAccessToken(appId, appSecret);
|
||||
const accessToken = 'c525e1c9125c429e83b699acd00ac858';
|
||||
getSessionKey(appId, accessToken, authorization_code);
|
||||
19
packages/decrypt-phone-by-data/src/index.ts
Normal file
19
packages/decrypt-phone-by-data/src/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { demoData } from './../demo';
|
||||
import { BizDataCrypt } from './decrypt';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const decryptPhoneNumber = (e: any, config?: { appId: string; sessionKey: string }) => {
|
||||
const { encryptedData, iv } = e.detail;
|
||||
console.log('Config:', config);
|
||||
const appId = config?.appId || 'your_app_id';
|
||||
const sessionKey = config?.sessionKey || 'your_session_key';
|
||||
const wxBizDataCrypt = new BizDataCrypt(appId, sessionKey);
|
||||
const decryptedData = wxBizDataCrypt.decryptData(encryptedData, iv);
|
||||
|
||||
console.log('Decrypted data:', decryptedData);
|
||||
};
|
||||
const appId = process.env.APP_ID;
|
||||
const sessionKey = 'TjJLWmxUaGdDMmFZYVE2eQ==';
|
||||
|
||||
decryptPhoneNumber({ detail: demoData }, { appId, sessionKey });
|
||||
48
pnpm-lock.yaml
generated
Normal file
48
pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,48 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
|
||||
packages/decrypt-phone-by-data:
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^24.3.3
|
||||
version: 24.3.3
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
dotenv:
|
||||
specifier: ^17.2.2
|
||||
version: 17.2.2
|
||||
|
||||
packages:
|
||||
|
||||
'@types/node@24.3.3':
|
||||
resolution: {integrity: sha512-GKBNHjoNw3Kra1Qg5UXttsY5kiWMEfoHq2TmXb+b1rcm6N7B3wTrFYIf/oSZ1xNQ+hVVijgLkiDZh7jRRsh+Gw==}
|
||||
|
||||
crypto-js@4.2.0:
|
||||
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
|
||||
|
||||
dotenv@17.2.2:
|
||||
resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
undici-types@7.10.0:
|
||||
resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@types/node@24.3.3':
|
||||
dependencies:
|
||||
undici-types: 7.10.0
|
||||
|
||||
crypto-js@4.2.0: {}
|
||||
|
||||
dotenv@17.2.2: {}
|
||||
|
||||
undici-types@7.10.0: {}
|
||||
Reference in New Issue
Block a user