如何使用 Javascript WebCrypto API 加载 PKCS#12 数字证书

2024-01-25

我正在尝试使用 WebCrypto API 签署数据,但我真的很想使用用户的 PKCS#12 来签署数据,而不是创建私钥/公钥并将其导出到 pkcs#1 或 8。我已经阅读了 W3C 规范,但无法充分理解它,也找不到任何关于如何执行此操作的好材料。现在我想把 ActiveX 和 Java Applet 放在一边。有没有办法调整以下内容:

var buffer = encode(prompt("Please enter your password"));
    //TODO:
    //implement a prompt for a pfx or cert

  return crypto.subtle.importKey("raw", buffer, "PBKDF2", false, usages);
    //TODO:
    //instead of importing it, ask for the certificate's pass to sign data
    //with crypto.subtle.sign

有什么指点吗?

UPDATE这是我一直在工作的代码

<script src="forge.min.js"></script>

<script>
    var errorsReportedByVerifier;
    errorsReportedByVerifier = checkStorage() && checkBrowserAPIs();
    if (!errorsReportedByVerifier){
        console.log("adding click event");
        document.getElementById('btnPfx').addEventListener('click', handlePFXFile, false);
        storeVariables();
        getVariables();
    }


    function handlePFXFile(evnt) {
        console.log("handling pfx")
        //alert(document.getElementById('pfx').value);

        //error happens in 1st line
        //error object does not accept property replace
        //forge.min.js Line 1, Column: 17823
        var p12Der = forge.util.decode64(document.getElementById('pfx').valueOf());
        //var pkcs12Asn1 = forge.asn1.fromDer(p12Der);
        //var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, 'pss');
        console.log("pkcs12");
    }
</script>

Web 加密 api 不支持 PKCS #12。您可以使用第三方库将 p12 解码为 forgehttps://github.com/digitalbazaar/forge#pkcs12 https://github.com/digitalbazaar/forge#pkcs12并在 webcrypto 中加载 privateKey

读取 PKCS#12 证书

PKCS#12 存储在 DER 中,因此首先从文件中读取它或使用预存储的 base64

//Reading certificate from a 'file' form field
var reader = new FileReader();
reader.onload = function(e) {               
    var contents = e.target.result;
    var pkcs12Der = arrayBufferToString(contents)
    var pkcs12B64 = forge.util.encode64(pkcs12Der);     
    //do something else...

}   
reader.readAsArrayBuffer(file);

function arrayBufferToString( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return binary;
}

//p12 certificate stored in Base64 format
var pkcs12Der= forge.util.decode64(pkcs12B64);

使用伪造解码 PKCS#12 并提取私钥

然后将DER格式解码为ASN1,让forge读取内容

var pkcs12Asn1 = forge.asn1.fromDer(pkcs12Der);
var pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);

然后从以下位置获取私钥pkcs12所需证书(请参阅 forge 文档)并转换为 PKCS #8 以使用 webcrypto 导入

// load keypair and cert chain from safe content(s) 
for(var sci = 0; sci < pkcs12.safeContents.length; ++sci) {
    var safeContents = pkcs12.safeContents[sci];

    for(var sbi = 0; sbi < safeContents.safeBags.length; ++sbi) {
        var safeBag = safeContents.safeBags[sbi];

        // this bag has a private key
        if(safeBag.type === forge.pki.oids.keyBag) {
            //Found plain private key
            privateKey = safeBag.key;
        } else if(safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {
            // found encrypted private key
            privateKey = safeBag.key;
        } else if(safeBag.type === forge.pki.oids.certBag) {
            // this bag has a certificate...        
        }   
    }
}

转换为 PKCS#8

function _privateKeyToPkcs8(privateKey) {
     var rsaPrivateKey = forge.pki.privateKeyToAsn1(privateKey);
     var privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
     var privateKeyInfoDer = forge.asn1.toDer(privateKeyInfo).getBytes();
     var privateKeyInfoDerBuff = stringToArrayBuffer(privateKeyInfoDer);
     return privateKeyInfoDerBuff;
 }
 function stringToArrayBuffer(data){
     var arrBuff = new ArrayBuffer(data.length);
     var writer = new Uint8Array(arrBuff);
     for (var i = 0, len = data.length; i < len; i++) {
         writer[i] = data.charCodeAt(i);
     }
     return arrBuff;
  }

在 Webcrypto 中导入密钥

最后导入webcrypto中的密钥

function _importCryptoKeyPkcs8(privateKey,extractable) {
    var privateKeyInfoDerBuff = _privateKeyToPkcs8(privateKey);

    //Import the webcrypto key
    return crypto.subtle.importKey(
            'pkcs8', 
            privateKeyInfoDerBuff, 
            { name: "RSASSA-PKCS1-v1_5", hash:{name:"SHA-256"}},
            extractable, 
            ["sign"]);        

}
_importCryptoKeyPkcs8(entry.privateKey,extractable).    
        then(function(cryptoKey) {
            //your cryptokey is here!!!
        });

电子签名

使用从上述方法返回的导入的 cryptoKey,您可以使用 webcrypto 进行签名。

var digestToSign = forge.util.decode64(digestToSignB64);
var digestToSignBuf = stringToArrayBuffer(digestToSign);

crypto.subtle.sign(
            {name: "RSASSA-PKCS1-v1_5"},
            cryptoKey,
            digestToSignBuf)
.then(function(signature){
    signatureB64 = forge.util.encode64(arrayBufferToString(signature))
});

我包含来自 base64 的编码,因为数据转换并不简单

在 pkc12 中,如果您需要构建 AdES 等高级格式,您还可以获得认证链

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Javascript WebCrypto API 加载 PKCS#12 数字证书 的相关文章

随机推荐

  • 使用 scapy 作为 MITM 即时更改数据包

    假设我设法处于客户端和服务器之间的通信中间 假设我打开一个热点并导致客户端仅通过我的机器连接到服务器 如何更改客户端发送和接收的数据包而不中断我自己与其他服务的通信 必须有一种方法可以通过我的脚本路由客户端发送和即将接收 在将它们转发给他之
  • Silverlight HTML Bridge 打印 window.print() 空白页

    我正在使用 HTML 桥 window print 打印 20 30 之间的范围 客户说正在打印空白页 我们只能在他们的机器上重现它 这是 xaml 中的代码 它将所有页面合并到一页中并打印它 这段代码可以工作并为我打印所有页面 我们只在
  • 对于跟踪和避免循环中的错误,您有哪些技巧?

    我刚刚发现 再次 一个实时浪费错误 如下 for int i 0 i lt length i Lots of code for int j 0 i lt length j Lots of code 您是否注意到前面的内部 i 应该是 j 我
  • TSQL md5 哈希与 C# .NET md5 不同

    我生成了一个 md5 哈希值 如下所示 DECLARE varchar varchar 400 SET varchar SELECT CONVERT VARCHAR 2000 HASHBYTES MD5 varchar 2 哪个输出 785
  • 目前不会命中断点,源代码与原始代码不同(即使在清理/重建之后)

    NET 4 控制台应用程序 该项目设置为 调试 模式 所设置的断点位于主 启动项目中 我在解决方案和项目级别上右键单击 gt 清理 然后右键单击 gt 重建 按照中的说明进行操作这个问题 https stackoverflow com qu
  • 在 Node.js 中请求相同模块时 require() 如何工作

    当 Node js 中多次需要一个模块时 它会返回相同的对象 因为require 缓存之前的调用 假设我有一个主记录器模块 可以注册子记录器模块 这些实际上是通过主记录器模块进行记录的log 功能 但这里不相关 我在主记录器模块中有类似的内
  • 如何从 android.support.v7.widget.Toolbar 中删除顶部和底部填充?

    我正在尝试放置一个SlidingTabLayout https github com google iosched blob master android src main java com google samples apps iosc
  • 如何在方法调用时将数组解包为不同的参数

    我想知道是否可以在接受 vargs 的方法调用上将对象数组解压到单独的对象中 这个问题类似于this one https stackoverflow com questions 6062618 java unpacking argument
  • 如果是超链接,则转到外部网站

    我在每一行的 gridview 中都有一个指向 aspx 页面的 视图 链接 根据资源类型 1 文件 或 2 超链接 它应该下载文件或转到提到的超链接
  • Angularjs HTML5 视频开放

    我正在加载 html5 mp4 视频 我想在视频结束时从角度范围触发功能 我尝试了下面的简单代码 但 onending 事件无法在角度范围内找到该函数 HTML
  • EJB3事务回滚

    我在 EJB3 无状态会话 bean 中使用 CMT 我还创建了自己的异常 并带有注释 ApplicationException rollback true 当我想回滚事务时 是否必须使用 context setRollbackOnly 我
  • 使用 seq2seq API 的 Tensorflow 序列到序列模型(版本 1.1 及更高版本)

    我在用着TensorFlow v 1 1 我想实施一个序列到序列使用 tf contrib seq2seq API 的模型 然而 我很难理解如何使用提供的所有函数 BasicDecoder Dynamic decode Helper Tra
  • 删除重复的重复字符

    我的存储过程中有一个字符串 例如 sam bob or 从上面的字符串中我必须从中删除多个逗号 它必须看起来像 sam bob 或者仅当 then 我必须仅使用 Sql Server 函数 我使用 Sql Server 2008 和 Net
  • ADODB SQL 语法 - 使用 Excel 工作表访问表内连接

    我有一个项目 用户需要填写 Excel 文件 然后将数据导出到 Access 数据库 Excel文件中收集的数据需要分3步导出 1 导出数据集1条记录 2 查询新导入记录的主键 自动编号 Access 3 导出数据设置 2 记录 其中包括填
  • 尾部斜杠给出内部服务器错误

    我希望我的所有页面都能正常工作 无论用户是否在末尾添加了尾部斜杠 以下行有效 RewriteRule index page 0 9 cmstut index php page 1 QSA L 但以下行会导致内部服务器错误 这是最后一行 该行
  • 对于 TBitmap,FMX 中是否有相当于 FloodFill 的功能?

    我正在从 VCL 转换为 FMX 在VCL中 TBitmap的TCanvas中有一个名为FloodFill的函数 它允许TBitmap的画布充满特定的颜色 直到在位图的画布上达到另一种特定的颜色 FMX 中有与此功能等效的函数吗 根据 RR
  • 如何列出相机可用的视频分辨率

    如果我的电脑上连接了多个摄像头 我想知道特定摄像头的最佳可用分辨率 例如 有些相机是高清或全高清 1 280 720像素 720p 或1920x1080像素 1080i 1080p 或者最常见的是网络相机 我想至少知道相机正常工作的最佳视频
  • Sitecore 页面编辑器 发布与内容相关的项目

    我有一个 产品页面 产品页面映射到 ProductPage Sitecore 项目 网站 页面 产品页面 我在该页面中有一个带有页面编辑器的文本区域 该区域从 Web 数据库中的 产品示例文本 Sitecore 项目的 描述 文本加载文本
  • 有没有办法用 Laravel 的 ELOQUENT ORM 来“限制”结果?

    有没有办法用 Laravel 的 ELOQUENT ORM 来 限制 结果 SELECT FROM games LIMIT 30 30 和雄辩 创建一个扩展 Eloquent 的 Game 模型并使用它 Game take 30 gt sk
  • 如何使用 Javascript WebCrypto API 加载 PKCS#12 数字证书

    我正在尝试使用 WebCrypto API 签署数据 但我真的很想使用用户的 PKCS 12 来签署数据 而不是创建私钥 公钥并将其导出到 pkcs 1 或 8 我已经阅读了 W3C 规范 但无法充分理解它 也找不到任何关于如何执行此操作的