SubtleCrypto: sign() 方法

Baseline 廣泛可用 *

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2020 年 1 月⁩ 起,所有主流瀏覽器均已支援。

* 此特性的某些部分可能存在不同級別的支援。

安全上下文: 此功能僅在安全上下文(HTTPS)中可用,且支援此功能的瀏覽器數量有限。

注意:此功能在 Web Workers 中可用。

sign()SubtleCrypto 介面的一個方法,用於生成數字 簽名

它接受以下引數:用於簽名的 金鑰、演算法特定的引數以及要簽名的資料。它返回一個 Promise,該 Promise 將以簽名結果 fulfilled。

您可以使用相應的 SubtleCrypto.verify() 方法來驗證簽名。

語法

js
sign(algorithm, key, data)

引數

algorithm

一個字串或物件,用於指定要使用的簽名演算法及其引數

  • 要使用 RSASSA-PKCS1-v1_5,請傳遞字串 RSASSA-PKCS1-v1_5 或形式為 { name: "RSASSA-PKCS1-v1_5" } 的物件。
  • 要使用 RSA-PSS,請傳遞一個 RsaPssParams 物件。
  • 要使用 ECDSA,請傳遞一個 EcdsaParams 物件。
  • 要使用 HMAC,請傳遞字串 HMAC 或形式為 { name: "HMAC" } 的物件。
  • 要使用 Ed25519,請傳遞字串 Ed25519 或形式為 { name: "Ed25519" } 的物件。
key

一個 CryptoKey 物件,其中包含用於簽名的金鑰。如果 algorithm 指定了公鑰加密系統,則此金鑰為私鑰。

data

一個 ArrayBuffer、一個 TypedArray 或一個 DataView 物件,其中包含要簽名的資料。

返回值

一個 Promise,它 fulfilled 一個包含簽名的 ArrayBuffer

異常

當遇到以下異常時,Promise 將被拒絕

InvalidAccessError DOMException

當簽名金鑰不是請求籤名演算法的金鑰,或者嘗試使用未知演算法或不適用於簽名的演算法時丟擲。

支援的演算法

Web Crypto API 提供以下可用於簽名和驗籤的演算法。

RSASSA-PKCS1-v1_5、RSA-PSS、ECDSA 和 Ed25519 是 公鑰加密系統,它們使用私鑰進行簽名,使用公鑰進行驗證。所有這些系統都使用 摘要演算法 在簽名之前將訊息雜湊為一個短的固定大小值。

  • 對於 RSASSA-PKCS1-v1_5 和 RSA-PSS,摘要演算法的選擇在呼叫 generateKey()importKey() 函式時傳入。
  • 對於 ECDSA,摘要演算法的選擇包含在傳遞給 sign() 函式的 algorithm 引數中。
  • 對於 Ed25519,摘要演算法始終是 SHA-512。

HMAC 演算法與其他演算法不同,它不是公鑰加密系統:它使用相同的演算法和金鑰進行簽名和驗證。這意味著驗證金鑰必須保密,而這又意味著此演算法不適用於許多簽名用例。然而,當簽名方和驗證方是同一實體時,它可能是一個不錯的選擇。

RSASSA-PKCS1-v1_5

RFC 3447 中指定了 RSASSA-PKCS1-v1_5 演算法。

RSA-PSS

RFC 3447 中指定了 RSA-PSS 演算法。

它與 RSASSA-PKCS1-v1_5 不同之處在於,它在簽名操作中包含了一個隨機鹽,因此用同一金鑰簽名同一訊息每次生成的簽名都會不同。在呼叫 sign()verify() 函式時,會傳入一個定義鹽長度的附加屬性。

ECDSA

ECDSA(橢圓曲線數字簽名演算法)是數字簽名演算法 (DSA) 的一個變體,在 FIPS-186 中指定,它使用了橢圓曲線密碼學(RFC 6090)。

簽名被編碼為 RFC 6090 中指定的 s1s2 值(在 RFC 4754 中分別稱為 rs),每個值都以大端位元組陣列的形式表示,其長度為曲線的位元大小向上舍入到最接近的整數字節。這些值按此順序連線在一起。

此編碼也是由 IEEE 1363-2000 標準提出的,有時也被稱為 IEEE P1363 格式。它與 X.509 簽名結構不同,後者是一些工具和庫(如 OpenSSL)預設生成的格式。

Ed25519

Ed25519 是一種基於 Curve25519 橢圓曲線的數字簽名演算法,該曲線屬於 RFC 8032 中定義的愛德華茲曲線數字簽名演算法 (EdDSA) 系列演算法。

HMAC

HMAC 演算法根據 FIPS 198-1 標準 (PDF) 計算和驗證基於雜湊的訊息認證碼。

要使用的摘要演算法在傳遞給 generateKey()HmacKeyGenParams 物件,或傳遞給 importKey()HmacImportParams 物件中指定。

HMAC 演算法對簽名和驗證使用相同的演算法和金鑰:這意味著驗證金鑰必須保密,這又意味著此演算法不適用於許多簽名用例。然而,當簽名方和驗證方是同一實體時,它可能是一個不錯的選擇。

示例

注意: 您可以在 GitHub 上 嘗試工作示例

RSASSA-PKCS1-v1_5

此程式碼獲取文字框的內容,對其進行編碼以便籤名,然後使用私鑰進行簽名。 在 GitHub 上檢視完整的原始碼。

js
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the sign operation.
*/
function getMessageEncoding() {
  const messageBox = document.querySelector(".rsassa-pkcs1 #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

let encoded = getMessageEncoding();
let signature = await window.crypto.subtle.sign(
  "RSASSA-PKCS1-v1_5",
  privateKey,
  encoded,
);

RSA-PSS

此程式碼獲取文字框的內容,對其進行編碼以便籤名,然後使用私鑰進行簽名。 在 GitHub 上檢視完整的原始碼。

js
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the sign operation.
*/
function getMessageEncoding() {
  const messageBox = document.querySelector(".rsa-pss #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

let encoded = getMessageEncoding();
let signature = await window.crypto.subtle.sign(
  {
    name: "RSA-PSS",
    saltLength: 32,
  },
  privateKey,
  encoded,
);

ECDSA

此程式碼獲取文字框的內容,對其進行編碼以便籤名,然後使用私鑰進行簽名。 在 GitHub 上檢視完整的原始碼。

js
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the sign operation.
*/
function getMessageEncoding() {
  const messageBox = document.querySelector(".ecdsa #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

let encoded = getMessageEncoding();
let signature = await window.crypto.subtle.sign(
  {
    name: "ECDSA",
    hash: { name: "SHA-384" },
  },
  privateKey,
  encoded,
);

HMAC

此程式碼獲取文字框的內容,對其進行編碼以便籤名,然後使用金鑰進行簽名。 在 GitHub 上檢視完整的原始碼。

js
/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the sign operation.
*/
function getMessageEncoding() {
  const messageBox = document.querySelector(".hmac #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

let encoded = getMessageEncoding();
let signature = await window.crypto.subtle.sign("HMAC", key, encoded);

Ed25519(金鑰生成、簽名和驗證)

此程式碼生成一個 Ed25519 簽名金鑰對,使用私鑰簽名文字 <input> 的(編碼後的)內容,然後使用公鑰驗證簽名。它改編自 GitHub 上的此原始碼,您可以在 此處線上執行

HTML

HTML 定義了一個包含要簽名文字的 <input> 元素,以及一個啟動操作(建立金鑰、簽名文字、然後驗證簽名)的按鈕。

html
<label for="message">Enter a message to sign:</label>
<input
  type="text"
  id="message"
  name="message"
  size="25"
  value="The lion roars near dawn" />

<input id="sign-button" type="button" value="Run" />

JavaScript

JavaScript 首先獲取 #sign-button#message <input> 元素,然後為按鈕的 click 事件新增一個監聽器。事件處理程式會清除日誌並執行其他操作,將 <input> 元素的內容傳遞過去。

js
const button = document.querySelector("#sign-button");
const input = document.querySelector("#message");

button.addEventListener("click", () => {
  // Clear log
  logElement.innerText = "";
  logElement.scrollTop = logElement.scrollHeight;
  // Run test
  test(input.value);
});

首先,它使用 Ed25519 演算法生成金鑰,然後對文字進行編碼並使用私鑰對該文字進行簽名。最後,它使用公鑰呼叫 SubtleCrypto.verify() 來驗證簽名。

js
async function test(data) {
  log(`Message: ${data}`);
  try {
    // Generate keys
    const { publicKey, privateKey } = await crypto.subtle.generateKey(
      {
        name: "Ed25519",
      },
      true,
      ["sign", "verify"],
    );

    log(`publicKey: ${publicKey}, type: ${publicKey.type}`);
    log(`privateKey: ${privateKey},  type: ${privateKey.type}`);

    // Encode data prior to signing
    const encoder = new TextEncoder();
    encodedData = encoder.encode(data);

    // Log the first part of the encoded data
    const shorterEncodedBuffer = new Uint8Array(encodedData.buffer, 0, 14);
    log(
      `encodedData: ${shorterEncodedBuffer}...[${encodedData.byteLength} bytes total]`,
    );
    // log(`encodedData: ${encodedData}`);

    // Sign the data using the private key.
    const signature = await crypto.subtle.sign(
      {
        name: "Ed25519",
      },
      privateKey,
      encodedData,
    );

    // Log the first part of the signature data
    const signatureBuffer = new Uint8Array(signature, 0, 14);
    log(
      `signature: ${signatureBuffer}...[${signature.byteLength} bytes total]`,
    );

    // Verify the signature using the public key
    const verifyResult = await crypto.subtle.verify(
      {
        name: "Ed25519",
      },
      publicKey,
      signature,
      encodedData,
    );

    // Log result - true if the text was signed with the corresponding public key.
    log(`signature verified?: ${verifyResult}`);
  } catch (error) {
    log(error);
  }
}

結果

規範

規範
Web 加密級別 2
# SubtleCrypto-method-sign

瀏覽器相容性

另見