只要项目大一点或者跟金融沾点边,都要考虑数据传输的安全性。
「用 POST 请求」。对,这是入门级的安全措施。还有没有高级一点的?(想要 GET 请求安全也不是不可能)
「那就用 HTTPS传输」。很对,这是一个非常重要的方案,这已经是中级的安全方案。
有没有中高级的安全方案?有,就是数据签名与验签。如果对接过支付宝支付、微信支付的朋友都很熟悉,如果没有对接过,我就跟大家聊聊。
我知道大家都很忙,没有时间听我啰嗦,我先把核心代码放出来,你先把功能实现了,回过头来细细听我唠叨。
一、private key 和 public key
大家可以通过如下这个网站生成 private key 和 public key(或者直接用我粘出来的两个 key 也行):http://web.chacuo.net/netrsakeypair
const privateKeyString = `
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIYSs8zydPxowvlnX+9vyDRS52nNwQocDcmpXyVfMY5F+1UdmVcktwbWAzjpAZHlmNjtSYYxMkp1jedDCFBVvcQGu1ulbT7yvGY8FFRfMG0UbXT7m3hUK6IHCVYUWKCGI2+HI44V26O+pq/KvIY96Oy+X1zZa1cv2aEb3GFnH+D1AgMBAAECgYAq8li4AL5qkCBMhcmcSCFIaXoJUUhRtbTQ8TkyHnEgUth0ZlvVJ0SdovY7R6AiHPq+GhxgKOgkI83F05oZKa30YUXY5MVhUYbBvjeifbTG3lIeDwL1s51nRIUBcGEyfnDfotePtt4quxV5cXNUcsNVymgA7Pin/6bGsc062ZsQgQJBAN6VGh39fDVzqHqhPpf4WsgOvmSC5RzPMobNEP0lU9SNat86Othg6tz3h3u3vRNsXMu+n+9F5oRFbJobiXhlt6ECQQCaM8P3bYpKwImAm0gmUixIoF0wwlnYm7PHcr5P1hYHrMvxS7dT5PdHKihJAnIYhtLG6bfdzFv/Kb4XP0Gf5xjVAkBLOk2XcUL3td1thO3o4xGbqBAFXJAfCpBjKw/g3yrUHe/O/plA5JC8mhR6ZgFLfUZnvkfD0PY2IliwRTpTLN3BAkBSxAYjAACCLuWeybnoF6L9OFXMngRrZucP3l6Xq2kXpX+xe9pihTrUT6Rfy5hB4duwODIgMlgOlPEauTEYCoohAkEAgmSF5pYELbvOW9eO9tQrFm20qf/9sXHi1Z87qSzGxA3HqcrqPe73L3nfxg5nrgOwg0ZqNl1wNBXti+LxXa7DeQ==
-----END PRIVATE KEY-----
`
const publicKeyString = `
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGErPM8nT8aML5Z1/vb8g0UudpzcEKHA3JqV8lXzGORftVHZlXJLcG1gM46QGR5ZjY7UmGMTJKdY3nQwhQVb3EBrtbpW0+8rxmPBRUXzBtFG10+5t4VCuiBwlWFFighiNvhyOOFdujvqavyryGPejsvl9c2WtXL9mhG9xhZx/g9QIDAQAB
-----END PUBLIC KEY-----
`
注意:
1、如果你公司的后端合适的是 Java 语言,就使用「PKCS8」密钥格式,也叫 「PKCS#8」,如果非 Java 语言可以考虑「PKCS1」。
2、Java 使用private key 和 public key时,要把首尾「-----BEGIN PRIVATE KEY-----」之类的删除,但在 JavaScript 里使用时,一定要加上,后面会细讲原因。
二、前端(APP端)加密
一般都是大前端使用 private key
生成加密数据 ,传给后端接口,后端使用 public key
验证加密信息,所以我只需要准备编写如下代码即可:
import { KEYUTIL, KJUR, hextob64, hextob64u } from 'jsrsasign';
const privateKeyString = `
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIYSs8zydPxowvlnX+9vyDRS52nNwQocDcmpXyVfMY5F+1UdmVcktwbWAzjpAZHlmNjtSYYxMkp1jedDCFBVvcQGu1ulbT7yvGY8FFRfMG0UbXT7m3hUK6IHCVYUWKCGI2+HI44V26O+pq/KvIY96Oy+X1zZa1cv2aEb3GFnH+D1AgMBAAECgYAq8li4AL5qkCBMhcmcSCFIaXoJUUhRtbTQ8TkyHnEgUth0ZlvVJ0SdovY7R6AiHPq+GhxgKOgkI83F05oZKa30YUXY5MVhUYbBvjeifbTG3lIeDwL1s51nRIUBcGEyfnDfotePtt4quxV5cXNUcsNVymgA7Pin/6bGsc062ZsQgQJBAN6VGh39fDVzqHqhPpf4WsgOvmSC5RzPMobNEP0lU9SNat86Othg6tz3h3u3vRNsXMu+n+9F5oRFbJobiXhlt6ECQQCaM8P3bYpKwImAm0gmUixIoF0wwlnYm7PHcr5P1hYHrMvxS7dT5PdHKihJAnIYhtLG6bfdzFv/Kb4XP0Gf5xjVAkBLOk2XcUL3td1thO3o4xGbqBAFXJAfCpBjKw/g3yrUHe/O/plA5JC8mhR6ZgFLfUZnvkfD0PY2IliwRTpTLN3BAkBSxAYjAACCLuWeybnoF6L9OFXMngRrZucP3l6Xq2kXpX+xe9pihTrUT6Rfy5hB4duwODIgMlgOlPEauTEYCoohAkEAgmSF5pYELbvOW9eO9tQrFm20qf/9sXHi1Z87qSzGxA3HqcrqPe73L3nfxg5nrgOwg0ZqNl1wNBXti+LxXa7DeQ==
-----END PRIVATE KEY-----
`
export function sha256withRSA(inputString) {
const key = KEYUTIL.getKey(privateKeyString);
// 创建 Signature 对象,设置签名编码算法
const signature = new KJUR.crypto.Signature({ alg: 'SHA256withRSA' });
// 初始化
signature.init(key);
// 传入待加密字符串
signature.updateString(inputString);
// 生成密文
const originSign = signature.sign();
// const sign64 = hextob64(originSign);
// console.log('sign base64 =======', sign64);
// const sign64u = hextob64u(originSign);
// console.log('sign base64u=======', sign64u);
return originSign;
}
把 public key 给后端同事,注意:不要带首尾 -----
两行。
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGErPM8nT8aML5Z1/vb8g0UudpzcEKHA3JqV8lXzGORftVHZlXJLcG1gM46QGR5ZjY7UmGMTJKdY3nQwhQVb3EBrtbpW0+8rxmPBRUXzBtFG10+5t4VCuiBwlWFFighiNvhyOOFdujvqavyryGPejsvl9c2WtXL9mhG9xhZx/g9QIDAQAB
到这儿,差不多就可以了收工了。
------------------------------- 好奇少年分割线 -------------------------------
三、jsrsasign API 简介
3.1 谁加密,谁验证?
发起方 | 动作 | key |
---|---|---|
大前端 |
加密 |
用 private key |
大前端 | 验证加密 | 用 public key |
后端 | 加密 | 用 private key |
后端 |
验证加密 |
用 public key |
3.2 KEYUTIL, KJUR, hextob64, hextob64u 分别都是什么
-
KEYUTIL
是 RSA/ECC/DSA key 的工具类, https://kjur.github.io/jsrsasign/api/symbols/KEYUTIL.html -
KJUR
是 Java Cryptographic Extension(JCE) 风格的 MessageDigest(Java 信息摘要算法)/ Signature(Java 签名)类和工具集(同时也是作者的网名),https://kjur.github.io/jsrsasign/api/symbols/KJUR.html -
hextob64
是将 hex 转了 Base 64 的方法,不建议使用encodeURIComponent
,这种编码方式会更大程度上扩大原数据的体积(Base 64只会增加1/3, 而 url 采用的 16 进制方式, 会增加1倍, 具体原因可另外谷歌) https://kjur.github.io/jsrsasign/api/symbols/Base64x.html -
hextob64u
跟上面的方法相比 ,解决了 URL 传输过程中特殊字符转码的问题。具体操作是:会将+
替换成-
,/
替换成_
,去掉尾部补全的=
,所以 Java 后端需要做反替换
后,验证加密字符的操作。详情请参考源码207行:https://kjur.github.io/jsrsasign/api/symbols/src/base64x-1.1.js.html
3.3 KEYUTIL.getKey 作用
API:KEYUTIL.getKey(param, passcode, hextype)
param:通过入参获取 private key 或 public key 对象
passcode:传入 private key 或 public key 生成时的密码(可选参数)
hextype: 传入pkcs8prv
、pkcs5prv
、pkcs8pub
、x509pub
之类的值(可选参数)
更多详情:https://kjur.github.io/jsrsasign/api/symbols/KEYUTIL.html#.getKey
3.4 KJUR.crypto.Signature 作用
KJUR.crypto.Signature
非常简单的「模拟」了 java.security.Signature 类,跟 Java 一样,在 new 时给 constructor 传参
API:KJUR.crypto.Signature(param)
- param 是一个对象,格式为:{ alg: 'SHA256withRSA' } 或 { alg: 'SHA256withRSA', prov: 'cryptojs/jsrsa' }
- alg 参数支持如下算法:
MD5withRSA - cryptojs/jsrsa
SHA1withRSA - cryptojs/jsrsa
SHA224withRSA - cryptojs/jsrsa
SHA256withRSA - cryptojs/jsrsa
SHA384withRSA - cryptojs/jsrsa
SHA512withRSA - cryptojs/jsrsa
RIPEMD160withRSA - cryptojs/jsrsa
MD5withECDSA - cryptojs/jsrsa
SHA1withECDSA - cryptojs/jsrsa
SHA224withECDSA - cryptojs/jsrsa
SHA256withECDSA - cryptojs/jsrsa
SHA384withECDSA - cryptojs/jsrsa
SHA512withECDSA - cryptojs/jsrsa
RIPEMD160withECDSA - cryptojs/jsrsa
MD5withRSAandMGF1 - cryptojs/jsrsa
SHA1withRSAandMGF1 - cryptojs/jsrsa
SHA224withRSAandMGF1 - cryptojs/jsrsa
SHA256withRSAandMGF1 - cryptojs/jsrsa
SHA384withRSAandMGF1 - cryptojs/jsrsa
SHA512withRSAandMGF1 - cryptojs/jsrsa
RIPEMD160withRSAandMGF1 - cryptojs/jsrsa
SHA1withDSA - cryptojs/jsrsa
SHA224withDSA - cryptojs/jsrsa
SHA256withDSA - cryptojs/jsrsa
2.5 signature.init(key) 作用
-
init(key, pass)
- 用密钥来初始化此对象,用于后面的加密或验签工作。只需指定密钥,该方法就会调用KEYUTIL
类初始化它。-
key - 可以传下述类型参数:
- PEM 格式的 PKCS#8 加密 RSA/ECDSA private key 必须包含 "
BEGIN ENCRYPTED PRIVATE KEY
" - PEM 格式的 PKCS#5 加密 RSA/DSA private key 必须包含 "
BEGIN RSA/DSA PRIVATE KEY
" 和 ",ENCRYPTED
" - PEM 格式的 PKCS#8 字符串 RSA/ECDSA private key 必须包含 "
BEGIN PRIVATE KEY
"(注:例子中我采用的是这种,因为 jsrsasign 要求必传
) - PEM 格式的 PKCS#5 字符串 RSA/DSA private key 必须包含 "
BEGIN RSA/DSA PRIVATE KEY
" 但没有 ",ENCRYPTED
" - RSAKey 对象 private key
- KJUR.crypto.ECDSA 对象 private key
- KJUR.crypto.DSA 对象 private key
- PEM 格式的 PKCS#8 加密 RSA/ECDSA private key 必须包含 "
-
pass - 传入生成 private key 和 public key 时的密码,可选参数
-
2.6 signature.updateString(inputString) 作用
-
signature.updateString(inputString)
- 用于更新原字符串为签名或验证的字符串,这里还没有生成密文。
2.7 signature.sign() 作用
-
signature.sign()
- 以十六进制字符串形式返回数据更新的签名,到就可以跟后端同事联调了。
2.8 hextob64(originSign) 作用
-
hextob64(originSign)
- 将十六进制字符串签名转换为 Base 64,这样是为了方便传输(十六进制的签名有4k,但 Base 64 签名只有 2k )
参考:
- jsrsasign使用笔记(加密,解密,签名,验签) - https://www.jianshu.com/p/b32fc387d8ad
全文完,谢谢阅读!
网友评论