当前位置:首页 > WEB3 > 正文内容

Java实现比特币交易签名,原理与实践

eeo2026-05-24 20:39:58WEB310
摘要:

比特币交易的核心安全性依赖于数字签名技术,而Java作为企业级开发的主流语言,提供了丰富的加密库支持比特币交易的签名与验证,本文将从比特币交易签名的原理出发,详细介绍如何使用Java实现比特币交易的签...

比特币交易的核心安全性依赖于数字签名技术,而Java作为企业级开发的主流语言,提供了丰富的加密库支持比特币交易的签名与验证,本文将从比特币交易签名的原理出发,详细介绍如何使用Java实现比特币交易的签名过程,包括关键步骤、代码示例及注意事项。

比特币交易签名原理

比特币交易签名本质是非对称加密的应用,其核心流程如下:

密钥对生成

比特币使用椭圆曲线数字签名算法(ECDSA),每个用户拥有一对密钥:

  • 私钥:随机生成,用于对交易进行签名,必须严格保密。
  • 公钥:由私钥通过椭圆曲线算法(secp256k1)派生,用于验证签名,可公开。

交易数据哈希

交易签名前需对交易数据进行哈希处理:

  • 将交易序列化(遵循比特币交易格式),得到原始字节数组。
  • 对字节数组进行SHA-256哈希,再进行RIPEMD-160哈希(或仅SHA-256,取决于实现),得到交易哈希值Transaction Hash)。

ECDSA签名

使用私钥对交易哈希值进行ECDSA签名,生成签名数据(包含rs两个值,通常编码为DER格式)。

签名验证

交易广播到网络后,节点使用签名者的公钥验证签名:

  • 通过公钥、交易哈希和签名数据,验证签名是否有效(确保交易未被篡改且由私钥持有者发起)。

Java实现比特币交易签名

Java实现比特币交易签名需依赖以下工具:

  • Bouncy Castle:提供加密算法库(如ECDSA、secp256k1曲线)。
  • 比特币核心库:如bitcoinj(简化交易构建与签名)或手动实现交易序列化。

本文以手动实现为例,结合Bouncy Castle库,逐步完成交易签名。

1 环境准备

添加Bouncy Castle依赖(Maven)

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

导入关键类

import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.SecureRandom;

2 关键步骤实现

步骤1:生成比特币密钥对(secp256k1曲线)

比特币使用secp256k1椭圆曲线,其参数可通过Bouncy Castle获取:

public class BitcoinKeyPair {
    // secp256k1曲线参数
    private static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");
    private static final ECDomainParameters CURVE = new ECDomainParameters(
            CURVE_PARAMS.getCurve(),
            CURVE_PARAMS.getG(),
            CURVE_PARAMS.getN(),
            CURVE_PARAMS.getH()
    );
    /**
     * 生成比特币私钥(32字节,随机数)
     */
    public static BigInteger generatePrivateKey() {
        SecureRandom random = new SecureRandom();
        BigInteger privateKey;
        do {
            privateKey = new BigInteger(256, random);
        } while (privateKey.compareTo(BigInteger.ONE) <= 0 || 
                 privateKey.compareTo(CURVE.getN().subtract(BigInteger.ONE)) >= 0);
        return privateKey;
    }
    /**
     * 从私钥生成公钥(未压缩格式,0x04前缀 + x + y,共65字节)
     */
    public static byte[] getPublicKey(BigInteger privateKey) {
        ECPoint point = CURVE.getG().multiply(privateKey);
        byte[] x = point.getAffineXCoord().getEncoded();
        byte[] y = point.getAffineYCoord().getEncoded();
        byte[] publicKey = new byte[65];
        publicKey[0] = 0x04; // 未压缩格式标识
        System.arraycopy(x, 1, publicKey, 1, 32);
        System.arraycopy(y, 1, publicKey, 33, 32);
        return publicKey;
    }
}

步骤2:构建比特币交易

比特币交易包含输入(Vin)和输出(Vout),需遵循严格的序列化格式,以下是一个简化交易示例:

public class BitcoinTransaction {
    private int version; // 版本号(如1)
    private List<TransactionInput> inputs; // 交易输入
    private List<TransactionOutput> outputs; // 交易输出
    private int lockTime; // 锁定时间(0表示立即生效)
    // 交易输入类
    public static class TransactionInput {
        private byte[] prevTxHash; // 前一笔交易的哈希(小端序)
        private int prevTxOutIndex; // 前一笔交易的输出索引
        private byte[] scriptSig; // 签名脚本(签名时留空,签名后填充)
        private int sequence; // 序列号(0xFFFFFFFF表示非最终)
    }
    // 交易输出类
    public static class TransactionOutput {
        private long amount; // 输出金额(聪,1 BTC = 1亿聪)
        private byte[] scriptPubKey; // 公钥脚本(锁定脚本,如接收方地址的公钥哈希)
    }
    /**
     * 序列化交易(签名前需对原始交易签名,排除scriptSig)
     */
    public byte[] serializeForSigning() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            dos.writeInt(version);
            dos.writeVarInt(inputs.size());
            for (TransactionInput input : inputs) {
                dos.write(input.prevTxHash);
                dos.writeInt(input.prevTxOutIndex);
                // 签名时scriptSig长度为0
                dos.writeVarInt(0);
                dos.writeInt(input.sequence);
            }
            dos.writeVarInt(outputs.size());
            for (TransactionOutput output : outputs) {
                dos.writeLong(output.amount);
                dos.write(output.scriptPubKey);
            }
            dos.writeInt(lockTime);
        } catch (IOException e) {
            throw new RuntimeException("Transaction serialization failed", e);
        }
        return baos.toByteArray();
    }
}

步骤3:计算交易哈希

对序列化后的交易数据进行SHA-256哈希:

import java.security.MessageDigest;
public class TransactionHash {
    public static byte[] calculateHash(byte[] transactionData) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            return digest.digest(digest.digest(transactionData));
        } catch (Exception e) {
            throw new RuntimeException("Hash calculation failed", e);
        }
    }
}

步骤4:ECDSA签名

使用私钥对交易哈希进行签名,生成DER格式的签名数据:

public class TransactionSigner {
    /**
     * ECDSA签名(secp256k1曲线)
     */
    public static byte[] sign(BigInteger privateKey, byte[] hash) {
        ECDSASigner signer = new ECDSASigner();
        ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKey, BitcoinKeyPair.CURVE);
        signer.init(true, privKey); // true表示签名
        BigInteger[] sig = signer.generateSignature(hash);
        return encodeDER(sig[0], sig[1]); // 转换为DER格式
    }
    /**
     * 将r,s编码为DER格式
     */
    private static byte[] encodeDER(BigInteger r, BigInteger s) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            // DER结构:0x30 [总长度] 0x02 [r长度] [r] 0x02 [s长度] [s]
            byte[] rBytes = r.toByteArray();
            byte[] sBytes = s.toByteArray();
            int totalLength = 2 + rBytes.length + 2 + sBytes.length;
            dos.writeByte(0x30); // SEQUENCE标记
            dos.writeByte(totalLength);
            // r部分
            dos.writeByte(0x02); // INTEGER标记
            dos.writeByte(rBytes.length);
            dos.write(rBytes);
            // s部分
            dos.writeByte(0x02); // INTEGER标记
            dos.writeByte(sBytes.length);
            dos.write(sBytes);
        } catch (IOException e) {
            throw new RuntimeException("DER encoding failed", e);
        }
        return baos.toByteArray();
    }
}

步骤5:填充签名脚本

签名完成后,需将签名数据和公钥填充到输入的`script

    币安交易所

    币安交易所是国际领先的数字货币交易平台,低手续费与BNB空投福利不断!

扫描二维码推送至手机访问。

版权声明:本文由e-eo发布,如需转载请注明出处。

本文链接:http://www.e-eo.com/post/27336.html

分享给朋友: