前端RSA密鑰生成和加解密——window.crypto使用相關

来源:https://www.cnblogs.com/charleschwang/p/18331401
-Advertisement-
Play Games

title: 使用 abortNavigation 阻止導航 date: 2024/8/3 updated: 2024/8/3 author: cmdragon excerpt: 摘要:在Nuxt3中,abortNavigation是一個輔助函數,用於路由中間件內阻止不符合條件的頁面訪問,實現許可權控 ...


轉自簡書,原文地址,本文介紹window.crypto關於RSA方面的API。


crypto API支持常用的rsa、aes加解密,這邊介紹rsa的應用。

瀏覽器相容性

window.crypto需要chrome 37版本,ie 11,safari 11才支持全部API而基本的加解密在safari 7就可以。

生成公私鑰

crypto.subtle.generateKey(algorithm, extractable, keyUsages),其中:
1.algorithm參數根據不同演算法填入對應的參數對,rsa需要填入RsaHashedKeyGenParams對象包含有:

  • name,可選RSASSA-PKCS1-v1_5, RSA-PSS, or RSA-OAEP,這邊如果用於加解密是不支持舊的RSAES-PKCS1-v1_5的(jsencrypt.js支持),RSASSA-PKCS1-v1_5用於簽名

  • modulusLength,為rsa位數,推薦至少2048位(相當於112位的aes)才能較為安全,此參數最為影響性能,比如1024比2048快非常多,NIST建議目前的RSA秘鑰安全強度是2048位,如果需要工作到2030年之後,就使用3072位的秘鑰

  • publicExponent,一般直接為[0x01, 0x00, 0x01]

  • hash,摘要方式,可選SHA-256SHA-384SHA-512,這邊也允許SHA-1,但是因為其安全性所以基本不建議

2.extractable一般是true,表示是否允許以文本的方式導出key

3.keyUsages是一個數組,裡面可選encryptdecryptsign

window.crypto.subtle.generateKey(
    {
        name: "RSA-OAEP",
        modulusLength: 2048,
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
        hash: {
            name: "SHA-512" // 這邊如果後端使用公鑰加密,要註意與前端一致
        },
    },
    true,
    ["encrypt", "decrypt"] // must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
)

函數結果返回一個promise對象,如果是對稱加密會得到一個密鑰CryptoKey類型,這邊rsa會得到一個密鑰對CryptoKeyPair,它有2個CryptoKey成員,privateKeypublicKey,我們導出密鑰為文本或者加解密都將通過這2個成員對象。

導出公私鑰

window.crypto.subtle.exportKey(format, key),其中:
1.format可選rawpkcs8spkijwk,我們這邊在導出公鑰時選spki,私鑰選pkcs8

2.key就是上面CryptoKeyPairprivateKey或者publicKey
函數返回一個promise對象,結果是一個ArrayBuffer,這邊轉成pem風格。

// 導出私鑰
 window.crypto.subtle.exportKey(
    "pkcs8", // 公鑰的話這邊填spki
    key.privateKey // 公鑰這邊是publicKey
).then(function(keydata2) {
    let privateKey = RSA2text(keydata1, 1) // 私鑰pem
}).catch(function(err) {
    console.error(err)
})
// pem格式文本
function RSA2text(buffer, isPrivate = 0) {
    let binary = ''
    const bytes = new Uint8Array(buffer)
    const len = bytes.byteLength
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i])
    }
    const base64 = window.btoa(binary)
    let text = "-----BEGIN " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----\n" // 這裡-----BEGIN和-----是固定的
    text += base64.replace(/[^\x00-\xff]/g, "$&\x01").replace(/.{64}\x01?/g, "$&\n") // 中間base64編碼
    text += "\n-----END " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----" // 這裡-----END和-----是固定的
    return text
}

導入公私鑰

window.crypto.subtle.importKey(
format,
keyData,
algorithm,
extractable,
keyUsages
)
,其中:
1.format可選rawpkcs8spkijwk,對應之前生成時的選擇,我們這邊在導入公鑰時選spki,私鑰選pkcs8

2.keyData,即window.crypto.subtle.exportKey獲得的ArrayBuffer,由於在這裡時我們一般只有pem文本的,所以還需要做轉換成ArrayBuffer。

3.algorithm這邊我們是rsa,需要填入一個RsaHashedImportParams對象,這邊對應crypto.subtle.generateKey所需的RsaHashedKeyGenParams對象,含有:

  • name,都保持與之前一致
  • hash

4.extractablecrypto.subtle.generateKey

5.keyUsagescrypto.subtle.generateKey
函數返回一個promise對象,結果是一個CryptoKey

// 導入公鑰
const pub = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo5NwYVVSg6rmAIKoxvCI
4Rn7FYh0mOFrnr0q2+r99/ZGuYCj5b6FQ8BwaaU8XpRn/y3W7W2bCggNRwllWQ2r
dHIM+6vN2Yi/QYntKqbcRNlK1s02G2lw9pERaWi15+5P8+AFR8IHANm/Dd/19OlM
5FZ9hh+qG7FXFhV2i4r62pUZxhk6ykItOT16IH5poK9eEDhqsXZ+3UW6cGlxANgO
jHJEnZpNCI5tS/4kFhLogHvEd88MoapljL6cZXk3ZafvxgUwxI6BZIhlw0adh2sj
bByIHitjRxqKMDH7uSdV9zf8t5Wa0bZFcUpcb5Jx2QBWIlO1qP+Q4LLMbNvEHeBC
4wIDAQAB
-----END PUBLIC KEY-----"

const pemHeader = "-----BEGIN PUBLIC KEY-----" // 之前RSA2text函數裡面的頭尾標識,這個是公鑰的
const pemFooter = "-----END PUBLIC KEY-----"
const pemContents = pub.substring(pemHeader.length, pub.length - pemFooter.length) // 去除pem頭尾
// base64解碼
const binaryDer = window.atob(pemContents)
// 轉為ArrayBuffer二進位字元串
const binary = str2ab(binaryDer)
window.crypto.subtle.importKey(
    "spki", // 這邊如果私鑰則是pkcs8
    binary , 
    {
        name: "RSA-OAEP",
        hash: "SHA-512" // 保持一致
    },
    true, 
    ["encrypt"] // 用於加密所以是公鑰,私鑰就是decrypt
)

function str2ab(str) {
    const buf = new ArrayBuffer(str.length)
    const bufView = new Uint8Array(buf)
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i)
    }
    return buf
}

加密解密

加密crypto.subtle.encrypt(algorithm, key, data),其中:
1.algorithm,加解密只支持RSA-OAEP不支持RSAES-PKCS1-v1_5

2.key即公鑰的CryptoKey對象

3.data是一個BufferSource對象,不能直接是要加密的字元串。
結果是一個ArrayBuffer,可以使用window.btoa(String.fromCharCode(...new Uint8Array(e)))輸出為base64字元串

const enc = new TextEncoder()
const data = enc.encode("sucks") // 這邊將要加密的字元串轉為utf-8的Uint8Array
window.crypto.subtle.encrypt(
    {
        name: "RSA-OAEP"
    },
    publicKey, // 生成或者導入的CryptoKey對象
    data
)

解密crypto.subtle.decrypt(algorithm, key, data),基本同加密,這邊data對應為加密返回的ArrayBuffer,如果是base64字元串比如從後端加密過來的,就需要轉為Uint8Array。

function base64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4)
    const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/')

    const rawData = window.atob(base64)
    const outputArray = new Uint8Array(rawData.length)

    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i)
    }
    return outputArray
}

返回值同加密


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## Kotlin 運算符 - **用途**: 對變數和值執行操作。 - **示例**: ```kotlin var x = 100 + 50 // 150 ``` - **分類**: - **算術**: `+`, `-`, `*`, `/`, `%`, `++`, `--`. ... ...
  • 本文解析了 Vue3 組件初次渲染的流程,涵蓋應用程式初始化、核心渲染步驟,以及 vnode 的創建和渲染,探討了 Vue3 內部機制及其跨平臺實現的關鍵細節。 ...
  • 今天遇到一個問題,在使用codemirror對兩條文本內容進行對比時,有同事反饋在它的電腦上會顯示成:前面一半是正常顯示差異內容,而後面就變成了全部是新增的。 像這樣: 預期的對比結果是這樣: 我們觀察用於對比的兩個文本,實際上上面的文本都是去掉後面括弧中的內容,對比結果不應該表現成全部刪除全部新增 ...
  • title: 使用 clearError 清除已處理的錯誤 date: 2024/8/5 updated: 2024/8/5 author: cmdragon excerpt: 摘要:“文章介紹了clearError函數的作用與用法,用於清除已處理的錯誤並可實現頁面重定向,提升用戶體驗。通過示例展示 ...
  • title: 使用 addRouteMiddleware 動態添加中間 date: 2024/8/4 updated: 2024/8/4 author: cmdragon excerpt: 摘要:文章介紹了Nuxt3中addRouteMiddleware的使用方法,該功能允許開發者動態添加路由中間件 ...
  • Vue2存在源碼可維護性差、性能問題和API相容性不足等缺點。Vue3通過monorepo管理、TypeScript開發、性能優化和引入Composition API等方式,顯著提升了源碼可維護性、編程體驗、TypeScript支持和邏輯復用實踐,從源碼、性能和語法API三方面進行了優化。 ...
  • 最近練習了一些前端演算法題,現在做個總結,以下題目都是個人寫法,並不是標準答案,如有錯誤歡迎指出,有對某道題有新的想法的友友也可以在評論區發表想法,互相學習 ...
  • Vue 3在編譯template過程中,會通過patchFlags優化虛擬DOM更新,提升性能。這些標誌通過位運算進行操作,包括動態文本、類、樣式、屬性、靜態提升等。patchFlags的使用極大地提高了diff演算法的效率。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...