java - ECDSA 使用 HSM 签名并在 Java/Kotlin 中验证
问题描述
我有一个公钥和一个使用 CryptokiEx 从 HSM 生成的签名,我需要在 Java 中验证签名,但这样做有一些困难。
签名在 HSM 中签名
byte[] signature = null;
LongRef signLen = new LongRef();
CryptokiEx.C_SignInit(hSession, signMech, hPrivateKey);
/* determine the length of the signature */
CryptokiEx.C_Sign(hSession, hash, hashLen, null, signLen);
signature = new byte[(int) signLen.value];
CryptokiEx.C_Sign(hSession, hash, hashLen, signature, signLen);
输出
public key: 030F92CF7082006E46ADBB7C51AD12435110595C59C45518E35C794AD6578D4FEE
private key: 00BF9044CED80C5E358F3673C998417A564E52B8837C21FD5DA6DFA8E506983B03
signature: 7531DDE88945CC2431F69C237B7A5A52D042E44CFBE9FF2AC8C821E6EC507EC0ABC6AD1909A7C571E1C504261AEE5B92B3061D9AD7F921738C79806584CFD358
message hash: 68656c6c6f20776f726c64
构造公钥
fun constructPublicKey(compressedPKStr: String) :ECPublicKey {
var publicKeyStr = compressedToUncompressed(compressedPKStr).toHex()
publicKeyStr = publicKeyStr.substring(2)
val kf: KeyFactory = KeyFactory.getInstance("EC")
val pubKeyX = publicKeyStr.substring(0, publicKeyStr.length / 2)
val pubKeyY = publicKeyStr.substring(publicKeyStr.length / 2)
val x: ByteArray = pubKeyX.hexToByteArray()
val y: ByteArray = pubKeyY.hexToByteArray()
val w = ECPoint(BigInteger(1, x), BigInteger(1, y))
return kf.generatePublic(ECPublicKeySpec(w, ecParameterSpecForCurve("secp256k1"))) as ECPublicKey
}
@Throws(NoSuchAlgorithmException::class, InvalidParameterSpecException::class)
fun ecParameterSpecForCurve(curveName: String?): ECParameterSpec {
val params = AlgorithmParameters.getInstance("EC")
params.init(ECGenParameterSpec(curveName))
return params.getParameterSpec(ECParameterSpec::class.java)
}
fun compressedToUncompressed(compressedPKStr: String) :ByteArray {
val compKey = compressedPKStr.hexToByteArray()
var SPEC = ECNamedCurveTable.getParameterSpec("secp256k1")
val point = SPEC.curve.decodePoint(compKey)
val x: ByteArray = point.xCoord.encoded
val y: ByteArray = point.yCoord.encoded
// concat 0x04, x, and y, make sure x and y has 32-bytes:
// 0x04 to indicate uncompressed PK
return concat(byteArrayOf(0x04), x, y)
}
签名转换为 ASN.1 DER 格式
@Throws(NumberFormatException::class)
fun byteToBin(byte: Byte): String {
val number = java.lang.Byte.toUnsignedInt(byte)
val s2 = String.format("%8s", Integer.toBinaryString(number)).replace(' ', '0')
return s2
}
fun constructSignature(signatureStr: String) :ByteArray {
val signatureR = signatureStr.substring(0, signatureStr.length / 2)
val signatureS = signatureStr.substring(signatureStr.length / 2)
var signatureRData = signatureR.hexToByteArray()
val firstRByteBin = byteToBin(signatureRData[0])
if (firstRByteBin.first() == '1') {
signatureRData = concat(byteArrayOf(0x00), signatureRData)
}
var signatureSData = signatureS.hexToByteArray()
val firstSByteBin = byteToBin(signatureSData[0])
if (firstSByteBin.first() == '1') {
signatureSData = concat(byteArrayOf(0x00), signatureSData)
}
val b2 = signatureRData.size
val b3 = signatureSData.size
val b1 = b2 + b3 + 4
return concat(byteArrayOf(0x30, b1.toByte(), 0x02, b2.toByte()), signatureRData, byteArrayOf(0x02, b3.toByte()), signatureSData)
}
验证签名但结果为假
val testBytes = "68656c6c6f20776f726c64".hexToByteArray()
val publicKeyStr = "030F92CF7082006E46ADBB7C51AD12435110595C59C45518E35C794AD6578D4FEE"
val publicKey = constructPublicKey(publicKeyStr)
val signatureStr = "7531DDE88945CC2431F69C237B7A5A52D042E44CFBE9FF2AC8C821E6EC507EC0ABC6AD1909A7C571E1C504261AEE5B92B3061D9AD7F921738C79806584CFD358"
val signatureData = constructSignature(signatureStr)
val signature: Signature = Signature.getInstance("SHA256withECDSA")
signature.initVerify(publicKey)
signature.update(testBytes)
signature.verify(signatureData)
你知道我哪里错了吗?
解决方案
我发现哪里错了。消息需要在签名之前使用 SHA256 进行哈希处理
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
System.out.println("Something is wrong");
}
byte[] hash = digest.digest(dataBytes);
CryptokiEx.C_Sign(hSession, hash, hashLen, signature, signLen);
推荐阅读
- python-3.x - 从字典中设置类的属性
- python - 如何修复“UnicodeDecodeError: 'utf-8' codec can't decode byte 0xca”错误?
- c# - 在 Azure 函数中使用关联 ID 进行日志记录
- c - 如果子进程被阻塞怎么办,如何停止父进程?
- r - 替代将所有元素相互比较的 lapply?(R,ENMTools)
- ruby-on-rails - 将日期提前一个月到更短的月份时,如何提前到下个月的第一天?
- python - Jenkins 不在 python 脚本中运行 .exe
- graphql - 当子类型有参数时,使用 graphql-codegen-maven-plugin 生成 DTO 类
- javascript - 为什么在 for 循环中在 JS 画布上绘制任何内容会导致页面无休止地加载?
- polymer - Polymer/Lit-Element - Braintree 网关集成 - Web 组件问题