前后端RSA双向加解密踩坑记
未完成文章
博主原创文章,转载请说明出处,但是不说我也管不了你🏳️
前言
正文
正文标题1
正文副标题1
crypto web api
https://developer.mozilla.org/zh-CN/docs/Web/API/SubtleCrypto/decrypt#%E8%AF%AD%E6%B3%95
前端加密解密正常
//start
// 将 ArrayBuffer 转换为 Base64 字符串
function arrayBufferToBase64(buffer:any) {
let binary = '';
const bytes = new Uint8Array(buffer);
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
// 将二进制字符串转换为 ArrayBuffer
function str2ab(str:string) {
const buf = new ArrayBuffer(str.length);
const view = new Uint8Array(buf);
for (let i = 0; i < str.length; i++) {
view[i] = str.charCodeAt(i);
}
return buf;
}
// 将 PEM 公钥转换为 SPKI 格式(去掉头尾标识)
function pemToSpki(base64PublicKey:any) {
// 去掉公钥的头尾部分
const publicKeyPem = base64PublicKey.replace(/-----BEGIN PUBLIC KEY-----/g, '').replace(/-----END PUBLIC KEY-----/g, '').trim();
return atob(publicKeyPem); // 解码 Base64 内容
}
// 将 Base64 编码的公钥转换为 CryptoKey 对象
async function importPublicKey(base64PublicKey:string) {
const binaryDerString = window.atob(base64PublicKey); // Base64 解码
const binaryDer = str2ab(binaryDerString); // 将二进制字符串转换为 ArrayBuffer
// 导入公钥
const publicKey = await crypto.subtle.importKey(
'spki', // 这里的 spki 表示公钥
binaryDer, // 公钥的 ArrayBuffer
{
name: 'RSA-OAEP',
hash: 'SHA-256', // 哈希算法
},
true, // 是否可以导出
['encrypt'] // 密钥用途
);
return publicKey;
}
// 使用公钥进行 RSA 加密
async function rsaEncrypt(plaintext:string, publicKey:CryptoKey) {
const encoder = new TextEncoder();
const encodedData = encoder.encode(plaintext);
// 使用公钥进行加密
const encryptedData = await crypto.subtle.encrypt(
{
name: 'RSA-OAEP',
},
publicKey,
encodedData
);
return arrayBufferToBase64(encryptedData); // 转换为 Base64 字符串返回
}
// 将 Base64 编码的私钥转换为 CryptoKey 对象
async function importPrivateKey(base64PrivateKey) {
const binaryDerString = atob(base64PrivateKey); // 将 Base64 解码
const binaryDer = str2ab(binaryDerString); // 将二进制字符串转为 ArrayBuffer
// 导入私钥
const privateKey = await crypto.subtle.importKey(
'pkcs8', // 私钥格式
binaryDer, // 私钥的 ArrayBuffer
{
name: 'RSA-OAEP',
hash: 'SHA-256', // 默认的哈希算法
},
true, // 是否可以导出
['decrypt'] // 密钥用途
);
return privateKey;
}
// 将 Base64 编码的字符串转换为 ArrayBuffer
function base64ToArrayBuffer(base64) {
const binaryString = atob(base64);
const len = binaryString.length;
const buffer = new ArrayBuffer(len);
const view = new Uint8Array(buffer);
for (let i = 0; i < len; i++) {
view[i] = binaryString.charCodeAt(i);
}
console.log(buffer)
return buffer;
}
// 使用私钥进行 RSA 解密
async function rsaDecrypt(encryptedData, privateKeyBase64) {
const binaryData = base64ToArrayBuffer(encryptedData); // 将 Base64 编码的密文转换为 ArrayBuffer
const privateKey = await importPrivateKey(privateKeyBase64)
// 使用私钥进行解密
const decryptedData = await crypto.subtle.decrypt(
{
name: 'RSA-OAEP',
// Web Crypto API 默认使用 PKCS1Padding 填充方式
},
privateKey,
binaryData
);
// 解密后的数据是一个 ArrayBuffer,可以将其转换为字符串
const decoder = new TextDecoder();
return decoder.decode(decryptedData); // 返回解密后的字符串
}
//加密工具
async function rsaE(s:string,p:string):Promise<string> {
const pulicKey = await importPublicKey('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmZr6zJX28C8adGE7/pxYRZajhdlPXXX6TiVPyKABaN397WfDOfnYGmqDZu+o4xxCyeqckzp0kFTamEQ0BeZXV1W9RdOaspHBnl5dZUbsJpRuBaCnCJyMKeipBEaIZke9GmI8QuzUYuFFM2KV5MF4yj4N+sVLfZWdgAse54JpFHZ53VI7Kmywdcf/U9wNAKKvMTMwgHawv23q7hwGbHEu8omNM/d6OnFPHGWVzxfRxJ/F1Q2BJSRJgkt5Zwvu1r1TWMeHDKgaq6FZBE6C7LE/YeCHacU6ngwrdZZxiYtYoUvCPCPany4zOGojD5+6vJG3x+jZOp7bM+xthz1OdhX3OQIDAQAB');
// const result = await rsaEncrypt(s,pulicKey);
const result = 'AcMAET+gaIJu0VULNKPRW+vldyk9C5dJCzpmlLjtQHqXM59Y5bMxq87q1ogvxRel3FmSlf4lM8RyGSjPg0dJdQ1LjBdEx2lH003FXOdR0WQkggH54j8YkarwTjfM4Qsgfo2RvZevGhrt6uDViekdgL1vetRShoz54oSz6xpWibsJYhrG4eK1WriLLAHATn5QCILGAD5pnA/ARYR/9lAEFwO4OG1nvPAMv0ey9/cQo00kpR1v976y5RpWYv2A1IBX06cJgmG0+qHqu/6zT1SwqdBZm2FFe5MM5pB4qkvCZtpnM/K5VjVq1rumrC/7xOr0frzHhEZjqKxu0G/9Gw2TSQ=='
//const result = 'Sf+aCzDUZ3cIzpeRnLOvoWfUuHH3DlRjk1VnECs5J/F4UIH0+tlYqM8BRHC1NFrjeyTk5k2g+8Oxfj9yN3N3U/EODi7/eK9dF/Q0RlyPXOypUilPtj9l1ig7bRcEv/p4WQO8Bw7V55sIlT2dNGSiiauWcK6oCx9t8VkE9bGTNR772Ojdsbn2Mn3dbUa6u2sv6iUAfoNVIetzrR6U+eOV+Z/O8qP1u9a4alBUtxmJ6YuVijcV5tvDLccziWTwOLIgUYna6A9YgKpi+Ykk2NWy4SOd1SOTbG98+sV/xaotshLVi4lj8vWp0Z9CmgPNl6gOAMknHWWyB2R6wpKb/dZ0BA=='
const cc = await rsaDecrypt(result,'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZmvrMlfbwLxp0YTv+nFhFlqOF2U9ddfpOJU/IoAFo3f3tZ8M5+dgaaoNm76jjHELJ6pyTOnSQVNqYRDQF5ldXVb1F05qykcGeXl1lRuwmlG4FoKcInIwp6KkERohmR70aYjxC7NRi4UUzYpXkwXjKPg36xUt9lZ2ACx7ngmkUdnndUjsqbLB1x/9T3A0Aoq8xMzCAdrC/beruHAZscS7yiY0z93o6cU8cZZXPF9HEn8XVDYElJEmCS3lnC+7WvVNYx4cMqBqroVkEToLssT9h4IdpxTqeDCt1lnGJi1ihS8I8I9qfLjM4aiMPn7q8kbfH6Nk6ntsz7G2HPU52Ffc5AgMBAAECggEAA2/+ERZBj5Q0Rrq205621H7Xo7sOXUByxK0t1QbspSbDpByUeD+MvCGaVXNDT6rOHVPqY1qVaXX2qt3q3p9uILKW4OFIhtq/bx58P1QYHJBpimdzGXycX+M4phN4gbZ1B0o/wD0pzxksuebIxTTlsbv0BuLDqtiubMNC8qoe1SH9PUDMSUqf0oYAEF718a9mCiMFl5I0Xw+6Hg4ecznYG5hbaK+5/Ph19BVDl2FXY6BlYNvg3PjwHnsZCI/rfj0FZbgtzefS0AKZ4Wwzza3s/pvNWBShbJkrkMr/JV6nkH2EWOYtgZblQf5oVBSLNqmByrfI9u2md0K5QdKITqk8QQKBgQDKsH8a0rocfy+qBrXziTx8UaiCb6UjXt7955DBSxsL2YPGCTXpcXZaVyLZ58tdUnWw6lTCGpL16EhqzIl5/lWczA5dpnNYxns1FTjDyOmQBmx8j8+lR0yob03flidVnHPq7VwkTxbZn5rE09i+fsO3YxIOaHVl5WjsORiyvPYceQKBgQDCAY0wsOiPRKv/np2tTpT1+IN2K+jMoIkibrADEXHVFZTSQ+O5o50VRd3Kpk/rsPl9iYSJ6q515KNV4SUr4KDaytmUjvaOvLvbtaS1JURvtK8dOLNzIXM5+0w5ehzqco5AtQpdAWhu+hI66Tcl7Et3ua3OeJj4hMmgrDJrNT6AwQKBgQC3LJhBym0NcdfGhbgLtlumUjnCX1lTRNqrhYlpVhHEPLf80bgaF0MJU7QYT4WW+f2Ie81Lea6Uc252rhAT4sjGIhddhR66/Oy9xUb+kOSbF2MVkvXp/SyHD8fFpzI3CWQszIO4AI268KVy+azoQzFm2y6nPLnT82ZGDTeH2INmqQKBgBVB8EOOjtzr7GDE84gC3UWaZmUzaH3LdVtFf5nGdo+fnr/vWmuD5vlmv2euE4HC0YOU5uXpxhX66H7HfbgyE84FfhPOGmwHeBoZ7CNBIx+OT8XG6brKxoJq6648mCCETtv+OlV4iwXl9stntHg+TQKgYNAH3NMWUbV+SMQAgInBAoGBAKP2SXkuHI/ZUFIIvIMwr67TpHWLMtgAl8laV2sjuTX7f9I3Yj+RQvwxaWPvSUGZXrsx9jTsMe6KZbqEZi+ksism5ayC6DujNve5gZDGZBvfT4lnjh0XPU1mGxS8bM6e4h+T5Rvra5Z/AByk3FvgkY9EDgFAqSch7/SmPieVqrQP')
console.log(cc)
console.log(result)
return cc;
}
const y = rsaE('123456','1');
后端加密解密正常
1 | |
前端后端对相同内容加密后的base64编码长度相同
目前可能性最大的问题所在:前端后端加密解密时所使用的填充方式或者其他具体细节不一样,前端目前没有找到方式选项(默认OAEP),后端可选:RSA/ECB/OAEPWithSHA1AndMGF1Padding、RSA/ECB/PKCS1Padding、RSA/ECB/OAEPWithSHA-256AndMGF1Padding
附录
附录1
附录2
参考资料
- 作者、其他主要作者...,题名[文献类型标识]:引用页码,出版地:出版者,获取和访问路径,文献更新日期或者出版年 ↩︎