ECDSA与EDDSA签名验证问题解决方案

0 阅读6分钟

ECDSA与EDDSA签名验证问题解决方案

项目描述

本项目针对Wycheproof项目中发现的ECDSA(椭圆曲线数字签名算法)和EDDSA(爱德华兹曲线数字签名算法)签名验证问题提供了专业修复方案。在原有的签名解码阶段,缺少必要的校验步骤,导致攻击者可能通过添加或删除零字节来操纵签名,进而影响依赖签名验证的电子邮件发送等关键功能的安全性。

本解决方案通过对signature.js文件的更新,实现了严格的签名长度验证机制,从根本上杜绝了因签名格式异常而导致的安全漏洞,为使用椭圆曲线密码学的应用提供了可靠的签名验证保障。

功能特性

  • 签名长度校验增强:在签名解码阶段实施严格的长度检测,防止零字节的非法添加或删除
  • EDDSA签名验证:完整支持Ed25519曲线的EdDSA签名生成与验证流程
  • ECDSA签名验证:基于secp256k1曲线的ECDSA签名验证,集成SHA-256哈希算法
  • 十六进制数据解析:便捷的十六进制密钥和消息处理接口
  • 轻量级依赖:基于成熟的elliptic加密库,确保实现的规范性和安全性

安装指南

系统要求

  • Node.js 10.0.0 或更高版本
  • npm 6.0.0 或更高版本

安装步骤

  1. 克隆代码仓库:

    git clone https://github.com/tu_usuario/tu_repositorio.git
    
  2. 进入项目目录:

    cd tu_repositorio
    
  3. 安装项目依赖:

    npm install
    

核心依赖项

  • elliptic:提供ECDSA和EdDSA签名算法的JavaScript实现
  • hash.js:用于SHA-256等哈希函数的计算

使用说明

基础使用示例

// 1. 引入所需库
var elliptic = require('elliptic');
var eddsa = elliptic.eddsa;
var ec = new elliptic.ec('secp256k1');

// 2. EDDSA签名验证流程
var ed25519 = new eddsa('ed25519');
var publicKeyEd25519 = '7d4d0e7f6153a69b6242b522abbee685fda4420f8834b108c3bdae369ef549fa';
var key = ed25519.keyFromPublic(publicKeyEd25519, 'hex');

var message = '54657374';  // 十六进制格式消息
var signature = '7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d00';

console.log('EDDSA签名验证结果:', key.verify(message, signature));

// 3. ECDSA签名验证流程
var hash = require('hash.js');
var toArray = elliptic.utils.toArray;

var hashMsg = hash.sha256().update(toArray(message, 'hex')).digest();
var publicKeySecp256k1 = '04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9';
var pubKey = ec.keyFromPublic(publicKeySecp256k1, 'hex');

console.log('ECDSA签名验证结果:', pubKey.verify(hashMsg, signature));

典型使用场景

  • 安全电子邮件系统:确保邮件来源的真实性和完整性
  • 数字身份验证:验证用户身份凭证的合法性
  • 区块链交易验证:在链下签名验证场景中增强安全性
  • API请求认证:防止请求参数被篡改

API概览

方法描述
eddsa.keyFromPublic(key, encoding)从十六进制或字节数组创建EDDSA公钥对象
key.verify(message, signature)验证消息签名是否有效,返回布尔值
ec.keyFromPublic(key, encoding)从十六进制或字节数组创建ECDSA公钥对象
pubKey.verify(hash, signature)验证消息哈希的ECDSA签名

核心代码

签名长度验证核心逻辑

// signature.js - 签名长度验证增强模块
/**
 * 验证ECDSA/EDDSA签名的长度是否符合标准
 * @param {Uint8Array} signature - 待验证的签名数据
 * @param {string} algorithm - 签名算法类型 ('ed25519' 或 'secp256k1')
 * @returns {boolean} - 签名长度是否有效
 * @throws {Error} - 当签名长度不符合标准时抛出异常
 */
function validateSignatureLength(signature, algorithm) {
    const LENGTHS = {
        'ed25519': 64,    // Ed25519签名固定为64字节
        'secp256k1': 64   // secp256k1 ECDSA签名通常为64字节(R+S)
    };
    
    const expectedLength = LENGTHS[algorithm];
    if (!expectedLength) {
        throw new Error(`Unsupported algorithm: ${algorithm}`);
    }
    
    if (signature.length !== expectedLength) {
        throw new Error(
            `Invalid signature length: expected ${expectedLength} bytes, ` +
            `got ${signature.length} bytes. Possible zero-byte manipulation detected.`
        );
    }
    
    return true;
}

// EDDSA解码过程中的长度检查增强
function decodeEdDSASignature(signatureHex) {
    const signatureBytes = toArray(signatureHex, 'hex');
    validateSignatureLength(signatureBytes, 'ed25519');
    // 继续原有的解码逻辑...
    return signatureBytes;
}

// ECDSA解码过程中的长度检查增强
function decodeECDSASignature(signatureHex) {
    const signatureBytes = toArray(signatureHex, 'hex');
    validateSignatureLength(signatureBytes, 'secp256k1');
    // 继续原有的解码逻辑...
    return signatureBytes;
}

完整的EDDSA签名验证实现

// eddsa-verifier.js
var elliptic = require('elliptic');
var eddsa = elliptic.eddsa;

/**
 * Ed25519签名验证器类
 * 封装了Ed25519曲线的签名验证逻辑,包含长度预检机制
 */
class Ed25519Verifier {
    constructor(publicKeyHex) {
        this.ed25519 = new eddsa('ed25519');
        this.publicKey = this.ed25519.keyFromPublic(publicKeyHex, 'hex');
    }
    
    /**
     * 验证消息签名的有效性
     * @param {string} messageHex - 十六进制格式的消息
     * @param {string} signatureHex - 十六进制格式的签名
     * @returns {boolean} - 签名是否有效
     */
    verify(messageHex, signatureHex) {
        // 签名长度预检 - 防止字节操纵攻击
        const signatureBytes = elliptic.utils.toArray(signatureHex, 'hex');
        if (signatureBytes.length !== 64) {
            console.error(`Rejected: signature length ${signatureBytes.length} != 64`);
            return false;
        }
        
        // 执行实际的签名验证
        return this.publicKey.verify(messageHex, signatureHex);
    }
    
    /**
     * 批量验证多个签名
     * @param {Array<{message: string, signature: string}>} items - 验证项数组
     * @returns {Array<boolean>} - 各签名的验证结果
     */
    verifyBatch(items) {
        return items.map(item => this.verify(item.message, item.signature));
    }
}

// 使用示例
var verifier = new Ed25519Verifier('7d4d0e7f6153a69b6242b522abbee685fda4420f8834b108c3bdae369ef549fa');
var isValid = verifier.verify('54657374', '7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d00');
console.log('验证结果:', isValid);

完整的ECDSA签名验证实现

// ecdsa-verifier.js
var elliptic = require('elliptic');
var hash = require('hash.js');

/**
 * secp256k1 ECDSA签名验证器类
 * 包含SHA-256哈希计算和严格的签名长度验证
 */
class Secp256k1Verifier {
    constructor(publicKeyHex) {
        this.ec = new elliptic.ec('secp256k1');
        this.publicKey = this.ec.keyFromPublic(publicKeyHex, 'hex');
    }
    
    /**
     * 计算消息的SHA-256哈希值
     * @param {string} messageHex - 十六进制格式的消息
     * @returns {Uint8Array} - 哈希值字节数组
     */
    hashMessage(messageHex) {
        var toArray = elliptic.utils.toArray;
        return hash.sha256().update(toArray(messageHex, 'hex')).digest();
    }
    
    /**
     * 验证消息的ECDSA签名
     * @param {string} messageHex - 十六进制格式的原始消息
     * @param {string} signatureHex - 十六进制格式的签名
     * @returns {boolean} - 签名是否有效
     */
    verify(messageHex, signatureHex) {
        // 签名长度预检 - 防止字节操纵攻击
        const signatureBytes = elliptic.utils.toArray(signatureHex, 'hex');
        if (signatureBytes.length !== 64) {
            console.error(`Rejected: signature length ${signatureBytes.length} != 64`);
            return false;
        }
        
        // 计算消息哈希
        const messageHash = this.hashMessage(messageHex);
        
        // 执行签名验证
        return this.publicKey.verify(messageHash, signatureHex);
    }
    
    /**
     * 验证预计算哈希的签名(用于性能优化场景)
     * @param {Uint8Array} hashBytes - 消息哈希字节数组
     * @param {string} signatureHex - 十六进制格式的签名
     * @returns {boolean} - 签名是否有效
     */
    verifyHash(hashBytes, signatureHex) {
        const signatureBytes = elliptic.utils.toArray(signatureHex, 'hex');
        if (signatureBytes.length !== 64) {
            console.error(`Rejected: signature length ${signatureBytes.length} != 64`);
            return false;
        }
        
        return this.publicKey.verify(hashBytes, signatureHex);
    }
}

// 使用示例
var verifier = new Secp256k1Verifier('04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9');
var isValid = verifier.verify('54657374', '7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d00');
console.log('ECDSA验证结果:', isValid);

6HFtX5dABrKlqXeO5PUv/7l2KSkP9qkI/HFxsju0RdE=