SubtleCrypto:sign() 方法

基线 广泛可用

此功能已建立良好,可在许多设备和浏览器版本中使用。它自 2015 年 7 月.

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,在部分或所有支持的浏览器中可用。

sign() 方法是 SubtleCrypto 接口的方法,用于生成数字签名

它以 密钥、一些特定于算法的参数和要签名的数据作为参数。它返回一个 Promise,该承诺将通过签名来完成。

可以使用相应的 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,它通过包含签名的 ArrayBuffer 来完成。

异常

当遇到以下异常时,承诺将被拒绝

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

RSASSA-PKCS1-v1_5 算法在 RFC 3447 中定义。

RSA-PSS

RSA-PSS 算法在 RFC 3447 中定义。

它与 RSASSA-PKCS1-v1_5 不同,它在签名操作中包含一个随机盐,因此使用相同的密钥对同一消息进行签名不会每次都产生相同的签名。一个额外的属性定义了盐的长度,在调用 sign()verify() 函数时传递给它们。

ECDSA

ECDSA(椭圆曲线数字签名算法)是数字签名算法的变体,在 FIPS-186 中定义,它使用椭圆曲线密码学 (RFC 6090).

签名被编码为 RFC 6090 中指定的 s1s2 值(分别称为 RFC 4754 中的 rs),每个值都是大端字节数组,它们的长度是曲线的位大小向上取整到一个完整的字节数。这些值按此顺序连接在一起。

此编码也由 IEEE 1363-2000 标准提出,有时被称为 IEEE P1363 格式。它与 X.509 签名结构不同,后者是一些工具和库(如 OpenSSL)生成的默认格式。

Ed25519

Ed25519 是一种基于 Curve25519 椭圆曲线的数字签名算法,它是 RFC 8032 中定义的 Edwards-Curve 数字签名算法 (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 加密 API
# SubtleCrypto-method-sign

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅