前端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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...