js封装p7数字信封结构签名验签

Sm3Util源自sm-crypto,sm-crypto没有暴露gethash方法,将所有源码拿过来自己暴露,smjs为整个sm-crypto声明文件(.d.ts)

import * as asn1Cms from "@peculiar/asn1-cms";
import * as asn1X509 from "@peculiar/asn1-x509";
import * as asn1Schema from "@peculiar/asn1-schema";
import { Convert } from "pvtsutils";
import {sm2} from '../smjs';
import {Hex} from 'arraybuffer-encoding'
import {AsnConvert} from "@peculiar/asn1-schema";
import Sm3Util from "../utils/sm3-util";
import {Attributes} from "../asn/asn-attributes";


const {Encode, Decode} = Hex;

class Pkcs7SignSm2 {

    private static SM3_DEF_UID: string = '1234567812345678';

    /**
     * p7签名
     * @param cert b64编码证书
     * @param privateKey 16进制字符私钥(暂定)
     * @param originData 原文字符串
     */
    static sign(cert: string, privateKey: string, originData: string): string{
        // 解析base64证书信息
        const certHex = Convert.FromBase64(cert);
        const certificate = asn1Schema.AsnConvert.parse(certHex, asn1X509.Certificate);
        // 公钥信息
        const publicKey = Encode(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
        // 原文16进制字符串
        const originDataHex = new Buffer(originData).toString('hex');
        // 带公钥参数sm3 hash值
        const digest = Sm3Util.getHash(originDataHex, publicKey);

        // 待签名数据  摘要数据需要加type value 0420
        const attributes: Attributes = new Attributes([new asn1Cms.Attribute({
            attrType: '1.2.840.113549.1.9.4',
            attrValues: [Decode('0420' + digest)]
        })]);

        // 签名数据转16进制,修改源码不在sm-crypto内部转(可能存在问题)
        const attributeStr = Encode(AsnConvert.serialize(attributes));

        // 签名 der:true补全sm2签名r,s(tlv)
        const signData = sm2.doSignature(attributeStr, privateKey, {
            der: true,
            hash: true,
            publicKey: publicKey,
            userId: this.SM3_DEF_UID
        });

        const signedData = new asn1Cms.SignedData();

        // 构造数字信封
        signedData.encapContentInfo = new asn1Cms.EncapsulatedContentInfo({
            eContentType: '1.2.840.113549.1.7.1',
            eContent: new asn1Cms.EncapsulatedContent({
                single: new asn1Schema.OctetString(Decode(originDataHex))
            })
        });

        signedData.digestAlgorithms = new asn1Cms.DigestAlgorithmIdentifiers([
            new asn1Cms.DigestAlgorithmIdentifier({
                algorithm: '1.2.156.10197.1.401'
            })
        ]);

        signedData.signerInfos = new asn1Cms.SignerInfos([new asn1Cms.SignerInfo({
            version: 1,
            sid: new asn1Cms.SignerIdentifier({
                issuerAndSerialNumber: new asn1Cms.IssuerAndSerialNumber({
                    issuer: certificate.tbsCertificate.issuer,
                    serialNumber: certificate.tbsCertificate.serialNumber
                }),
            }),
            digestAlgorithm: new asn1Cms.DigestAlgorithmIdentifier({
                algorithm: '1.2.156.10197.1.401'
            }),
            signatureAlgorithm: new asn1Cms.SignatureAlgorithmIdentifier({
                algorithm: '1.2.156.10197.1.501'
            }),
            signature: new asn1Schema.OctetString(Decode(signData)),
            signedAttrs: attributes
        })]);

        const cms = new asn1Cms.ContentInfo({
            contentType: asn1Cms.id_signedData,
            content: asn1Schema.AsnConvert.serialize(signedData),
        });

        const raw = asn1Schema.AsnConvert.serialize(cms);

        return Convert.ToBase64(raw);
    }

    /**
     * p7验签
     * @param cert b64编码证书
     * @param signature b64编码签名数据
     */
    static verify(cert: string, signature: string): boolean {
        // 解析base64证书信息
        const certHex = Convert.FromBase64(cert);
        const certificate = asn1Schema.AsnConvert.parse(certHex, asn1X509.Certificate);
        // 公钥信息
        const publicKey = Encode(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);

        // 签名数据转arrayBuffer
        const arrayBuffer = Convert.FromBase64(signature);
        // 解析contentInfo
        const contentInfo = asn1Schema.AsnConvert.parse(arrayBuffer, asn1Cms.ContentInfo);
        // 解析signedData
        const signedData = asn1Schema.AsnConvert.parse(contentInfo.content, asn1Cms.SignedData);

        const eContent: any = signedData.encapContentInfo.eContent;
        // 原文ArrayBuffer
        const data = eContent.single.buffer;
        // 原文16进制字符
        const originDataHex = Encode(data);
        // 原文摘要计算
        const digest = Sm3Util.getHash(originDataHex, publicKey);
        // Attributes数据
        const attributes: Attributes = new Attributes(signedData.signerInfos[0].signedAttrs);

        // 将Attributes数据转换为自定义ASN结构
        const signedAttrs = AsnConvert.parse(AsnConvert.serialize(attributes), Attributes);
        const attrValues = signedAttrs[0].attrValues[0];

        // 摘要数据去掉tl 0420 与原文摘要对比
        const signDataDigest = Encode(attrValues).substr(4, 128);
        if (digest !== signDataDigest) {
            console.log("error");
            return false;
        }

        const signInfo = signedData.signerInfos[0].signature;
        // 签名数据
        const sign = Encode(signInfo.buffer);

        // 待签名数据
        const attributeStr = Encode(AsnConvert.serialize(signedAttrs));
        console.log("attributeStr2 " + attributeStr);

        return sm2.doVerifySignature(attributeStr, sign, publicKey,{
            der: true,
            hash: true,
            publicKey: publicKey,
            userId: this.SM3_DEF_UID
        });
    }
}

export default Pkcs7SignSm2;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值