ECDSA签名转换工具(给定R.hex与S.hex之后转为ASN.1格式)

工具介绍

Hex string形式输入R值,S值,运行后生成 ASN.1结果

R:
8a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b

S:
8b3f8e4e1a1c4b0d0c8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b10

ASN.1:
30460221008a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0221008b3f8e4e1a1c4b0d0c8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b10

界面展示

image

HTML source

<!DOCTYPE html>
<html>
<head>
    <title>ECDSA签名转换器</title>
    <meta charset="UTF-8">
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        .container { background-color: #f5f5f5; padding: 20px; border-radius: 8px; }
        .input-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        /* 修改输入框样式 */
        textarea {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            resize: vertical;
            min-height: 80px;
        }
        button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background-color: #45a049; }
        .result { margin-top: 20px; padding: 15px; background-color: #e8f5e9; border-left: 4px solid #4CAF50; white-space: pre-wrap; }
        .error { color: #d32f2f; background-color: #ffebee; padding: 10px; border-radius: 4px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>ECDSA签名转换器</h1>
        <div class="input-group">
            <label for="rValue">R值 (十六进制字符串):</label>
            <!-- 移除 placeholder,直接设置默认值 -->
            <textarea id="rValue">4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b</textarea>
        </div>
        <div class="input-group">
            <label for="sValue">S值 (十六进制字符串):</label>
            <!-- 移除 placeholder,直接设置默认值 -->
            <textarea id="sValue">3b3f8e4e1a1c4b0d0c8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b10</textarea>
        </div>
        <button onclick="convertToASN1()">生成ASN.1签名</button>
        
        <div id="resultContainer" style="display: none;">
            <h3>ASN.1格式签名:</h3>
            <!-- 将结果显示区域替换为 textarea -->
            <textarea id="asn1Result" readonly style="background-color: #e8f5e9; border-color: #4CAF50;"></textarea>
        </div>
        
        <div id="errorContainer" class="error" style="display: none;"></div>
    </div>

    <script>
        function convertToASN1() {
            // 获取输入值
            const rValue = document.getElementById('rValue').value.trim();
            const sValue = document.getElementById('sValue').value.trim();
            const resultContainer = document.getElementById('resultContainer');
            const errorContainer = document.getElementById('errorContainer');
            const asn1Result = document.getElementById('asn1Result');
            
            // 重置显示状态
            resultContainer.style.display = 'none';
            errorContainer.style.display = 'none';
            errorContainer.textContent = '';
            
            try {
                // 验证输入
                if (!rValue || !sValue) {
                    throw new Error("R值和S值不能为空");
                }
                
                if (!/^[0-9a-fA-F]+$/.test(rValue) || !/^[0-9a-fA-F]+$/.test(sValue)) {
                    throw new Error("输入必须是有效的十六进制字符串");
                }
                
                // 将十六进制字符串转换为字节数组
                const rBytes = hexToBytes(rValue);
                const sBytes = hexToBytes(sValue);
                
                // 构建ASN.1结构[2](@ref)
                const asn1Signature = buildASN1Signature(rBytes, sBytes);
                
                // 转换为十六进制字符串显示
                const hexResult = bytesToHex(asn1Signature);
                
                // 显示结果
                asn1Result.value = hexResult; // 修改为设置 textarea 的 value
                resultContainer.style.display = 'block';
            } catch (error) {
                errorContainer.textContent = "错误: " + error.message;
                errorContainer.style.display = 'block';
            }
        }
        
        // 构建ASN.1签名[2,3](@ref)
        function buildASN1Signature(rBytes, sBytes) {
            // 处理整数编码(确保首位不是负数)
            const encodeInteger = bytes => {
                if (bytes[0] & 0x80) {
                    return Uint8Array.from([0x00, ...bytes]);
                }
                return bytes;
            };
            
            const encodedR = encodeInteger(rBytes);
            const encodedS = encodeInteger(sBytes);
            
            // 构建TLV结构
            const tlv = [
                0x30, // SEQUENCE标签
                encodedR.length + encodedS.length + 4, // 总长度
                0x02, // INTEGER标签
                encodedR.length, // R长度
                ...encodedR,
                0x02, // INTEGER标签
                encodedS.length, // S长度
                ...encodedS
            ];
            
            return new Uint8Array(tlv);
        }
        
        // 十六进制字符串转字节数组
        function hexToBytes(hex) {
            if (hex.length % 2 !== 0) {
                throw new Error("十六进制字符串长度必须是偶数");
            }
            
            const bytes = new Uint8Array(hex.length / 2);
            for (let i = 0; i < bytes.length; i++) {
                bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
            }
            return bytes;
        }
        
        // 字节数组转十六进制字符串
        function bytesToHex(bytes) {
            return Array.from(bytes)
                .map(b => b.toString(16).padStart(2, '0'))
                .join('');
        }
    </script>
</body>
</html>
posted @ 2025-07-17 23:07  北壹  阅读(44)  评论(0)    收藏  举报