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

浏览器兼容性

另见