如何在瀏覽器中創建 X509 證書 , 既有樂趣又有收益

從開始到結束,本指南將告訴你如何在 Akash Network 創建一個有效的 x509 證書,瀏覽器中使用 ECDSA SHA-256。

第一印象感覺很枯燥,那可能是因爲作爲網絡開發者,我們一般不會處理這種東西。然而,隨着前端開發迅速發展成爲全棧應用開發,原來很多是傳統後端開發乾的活我們也要碰到了。

這項任務的關鍵要求是生成一對用於簽名和驗證的密鑰。以 PEM 格式導出私鑰、公鑰和證書,以便它們可以被存儲,或傳輸。

通過 Akash 網絡,我們將證書和公鑰存儲在鏈上,這樣其他人就可以驗證一個行爲者與他們聲稱是用戶的資金來源相連接。我們將這些 PEM 格式轉換成 uInt8 數組,通過 protobuf 傳輸。

在研究時,我認爲這將是一個簡單的互聯網搜索。然而,缺失和過時的信息結合在一起,使得我很難找到讓當前的算法與我想要通信的其他客戶 / 服務器的規格相匹配所需的信息。

讓我們先從簡單的東西開始。

生成一個密鑰對

今天我們這樣做的最常見的情況是,要麼驗證我們機器的身份,以代替密碼,如 Github、AWS 控制檯、CryptoCoins 或 Web 服務器。現在,我們要在瀏覽器中生成一個。

幸運的是生活是美好的,Chrome 和其他瀏覽器供應商在瀏覽器中實現密碼學庫方面有一個良好的開端。我們也有一些很好的標準化庫來處理多個地方的密碼學問題。我們將倚重的兩個庫是 pkijs[1] 和 asn1js[2]。

這些庫中的每一個都爲我們提供了所需的方法 / 類和類型,以處理一個任務的多個方面。我們可以使用你喜歡的軟件包管理器簡單地安裝這些庫。這些都能很好地捆綁在現代網絡瀏覽器上。我還接觸了 pvutils,以簡化其用於將證書編碼爲文本格式的一些效用。這個庫在整個密碼學部分並不需要,所以請隨意將借用的方法換成你自己喜歡的版本。

    npm i pkijs asn1js pvutils  

一旦安裝了這些工具,我們就可以開始工作了,生成我們的第一個密鑰對。

    const {   
        getCrypto,   
        getAlgorithmParameters   
    } = require(\"pkijs/build\");  

我們將在這裏使用 getCrypto 方法來給我們一個加密處理程序。在瀏覽器中,這通常映射到一個窗口。SubtleCrypto 也可以推斷爲 window.crypto.webKitSubtle,window.crypto.subtle。我們的想法是,你會得到 JavaScript 在整個瀏覽器中是如何標準化的這一點。所以我們要用 getCrypto 來簡化。

我們還需要獲取 getAlgorithmParameters,這樣我們就可以很容易地得到圍繞需要進行的具體編碼和解碼的摘要信息。這大大簡化了我們把所有曲線、散列數學等相關信息拉到一起的需要,使其發揮作用。值得慶幸的是,如果你能跟上 pkijs 的步伐,你應該可以獲得現代的、最新的算法。在這裏我要給你一點建議,遠離預置和瀏覽器捆綁的模塊生態系統之外的東西。如果你需要在密碼學方面有所發展,你會發現它們在幾年後就可能會過時。

下面是將生成一個加密密鑰對的代碼存根。在這裏,我將指定一個使用 SHA-256 散列的 ECDSA 簽名。你可能需要根據你的目標環境來改變這些。

    const crypto = getWebCrypto();  
    const algo = getAlgorithmParameters(\"ECDSA\", \"generatekey\");  
    const keyPair = crypto.generateKey(algorithm.algorithm, true, algorithm.usages);  

就是這樣,你剛剛生成了一個有效的密鑰對 !

生成備份

generateKey 方法的分類是 algo、allowExport 和 usage。由於 getAlgorithm 方法,其中兩個已經定義好了,我們可以自由決定是否要在代碼中捕獲這個密鑰對。在這種情況下,我們要這樣做,因爲我們要生成這些備份,我們還需要用這些密鑰來簽署和生成一個有效的 x509 證書。

生成 x509 比我預期的要簡單得多。最大的障礙是理解 OIDs 應用於證書創建的方式。對象 ID 是用來識別字段和哪種屬性應該被編碼到證書中的。

使用像 OID[3] 信息這樣的 OID 庫,你可以找到你需要的字段的 OIDs。有一些常見的字段,如組織、國家和州,你也可以進行編碼。這些字段的必要性是由證書的消費實體嚴格決定的。他們不需要產生一個可用於驗證和簽名的有效證書。

爲了生成 x509,我們將從 pkijs 中抓取一些更多的助手。我們還將從 asn1js 中獲取類型,所以我們也要把它帶在身邊。

    const {  
        AttributeTypeAndValue,  
        Certificate,  
        BasicConstraints,  
        Extension,  
        ExtKeyUsage  
    } = require(\"pkijs/build\");  
    const asn1js = require(\"asn1js\");  

證書類將被用來創建 x509,其他類將被用來裝飾和與證書互動。你可能不需要 ExtKeyUsage,但我們會去了解它,因爲這個方案也適合其他地方。

創建證書

創建實際證書是超級簡單的。

    const cert = new Certificate();  

現在,我們在內存中已經有了一個證書,我們可以正式開始構建證書所需的字段。

    cert.version = 2;  
    cert.serialNumber = new asn1js.Integer({ value: Date.now() });  

同樣,這些字段中的任何一個都是可選的。我們可以直接跳過簽署證書,這將使它完全有效,但你遇到的實際情況可能需要額外的字段和描述–版本和序列號是兩個非常常見的字段。

它們看起來是沒什麼特別作用的字段,但可以被驗證者用來確定證書的簽發者是否以任何有意義的方式改變了它。版本是 0 的偏移量,這裏的序列號只是發行的時間戳。根據使用情況,這可能需要更加積極(例如,JWT 替換)。

導出私鑰和公鑰

爲了導出私鑰和公鑰,我們要使用兩個標準。pkcs#8 標準,即公鑰密碼學標準。簡單說就是密鑰信息的 base64 編碼版本。結合 header 和 footer 作爲邊界,每隔 64 字節有一個換行符。

我們還將使用 spki 標準,這意味着我們希望我們的公鑰是在證書之外容易交換的格式。spki 的目的是爲了允許在證書頒發者之外進行密鑰驗證。

從瀏覽器中獲取這兩個二進制文件很簡單,只需幾行代碼。

    const spki = await crypto.exportKey(\"spki\", keyPair.privateKey);  
    const pkcs8 = await crypto.exportKey(\"pkcs8\", keyPair.privateKey);  

將二進制輸出結構化爲文本形式

現在,我們需要將這些二進制輸出結構化爲具有我們稱之爲邊界的文本形式。在這種情況下,邊界是一個帶有新線的文本分界線,如下圖所示。

    -----BEGIN CERTIFICATE-----\\n  
    -----END CERTIFICATE-----  

在這兩個邊界之間,我們要把我們的證書和鑰匙的 base64 表示法放進去。還記得 pvutils 嗎?現在是時候去找那些工具了 !

    const { arrayBufferToString, toBase64 } = require(\"pvutils\");  

我們還需要以 64 字節的行數限制來格式化 PEM 文件。這真的很容易做到,用一個替換的 regex。

    function formatPEM(pemString: any) {  
       return pemString.replace(/(.{64})/g, \"$1\\n\");  
    }  

歸途

讓我們一起使用這三個輔助方法,將 ArrayBuffers 轉換爲字符串,然後我們可以對這些字符串進行 Base64 編碼,併爲創建我們的 PEM 文件而格式化它們。

    const pems = {  
    csr: `-----BEGIN CERTIFICATE-----\\n${formatPEM(  
       toBase64(arrayBufferToString(certBER))  
    )}\\n-----END CERTIFICATE-----`,  
    privateKey: `-----BEGIN PRIVATE KEY-----\\n${formatPEM(  
       toBase64(arrayBufferToString(pkcs8))  
    )}\\n-----END PRIVATE KEY-----`,  
    publicKey: `-----BEGIN EC PUBLIC KEY-----\\n${formatPEM(  
       toBase64(arrayBufferToString(spki))  
    )}\\n-----END EC PUBLIC KEY-----`,  
    };  

這段代碼使用模板字面量,將 String 格式的邊界與返回的 base64 編碼結合起來。剩下的是一個擁有 3 個不同的 PEM 格式文件的對象,這些文件可以保存在內存中或寫入文件中進行傳輸。

可以簽名啦!

想讓這一切變得更簡單,或者也許正在尋找一種方法來生成與 Akash 網絡兼容的證書?推薦使用 AkashJS[4],它旨在幫助簡化瀏覽器和 NodeJS 下的這些任務。

微信外鏈

[1]

pkijs:https://github.com/PeculiarVentures/PKI.js

[2]

asn1js:https://www.npmjs.com/package/asn1js

[3]

OID:http://www.oid-info.com/

[4]

AkashJS:http://github.com/ovrclk/akashjs

Akash 相關鏈接

中文鏈接

    微博 : https://weibo.com/akashchina  
    幣乎 : https://bihu.com/people/1117023356  
    推特 : https://twitter.com/AkashCommunity  
    QQ 羣 : http://t.hk.uy/sqe  
    語雀:https://www.yuque.com/akashnetwork/index  
    akash 官網:https://akash.network/?lang=zh-hans  

英文鏈接:

    Twitter: https://twitter.com/akashnet_  
    Facebook: https://www.facebook.com/akashnw/  
    LinkedIn: https://www.linkedin.com/company/akash-network/  
    Telegram: https://t.me/AkashNW  
    Github: https://github.com/ovrclk  

如何在瀏覽器中創建 X509 證書 , 既有樂趣又有收益掃碼關注 Akash