【问题标题】:How to generate 33-byte compressed NIST P-256 public key?如何生成 33 字节压缩的 NIST P-256 公钥?
【发布时间】:2019-05-06 20:37:27
【问题描述】:

我需要生成这样的公钥并对字节进行额外的签名(这将包括这个之前生成的密钥)

我需要构造以下字节: ASN.1前缀+(33字节压缩NIST P-256公钥)的签名

签名应该从其他定义的私钥传递

ECDSA 规范:

●曲线:

NIST P-256 也称为 secp256r1 和 prime256v1 (openssl)

● 签名格式 ASN.1。 ECDSA 签名的 r 和 s 值必须为正 整数和 DER 编码。

Android 中是否有 API 来执行此过程?那我该怎么用呢?

我尝试了什么:

 try {
        val generator = KeyPairGenerator.getInstance("ECDSA")
        val ecSpec = ECNamedCurveTable
                .getParameterSpec("prime256v1")
        generator.initialize(ecSpec)
        val keyPair = generator.generateKeyPair()

        val privKey = keyPair.private
        val encodedPrivKey = privKey.encoded
        System.out.println(toHex(encodedPrivKey))

        val pubKey = keyPair.public
        val encodedPubKey = pubKey.encoded
        System.out.println(toHex(encodedPubKey))

        val keyFactory = KeyFactory.getInstance("ECDSA")
        val pubKey2 = keyFactory.generatePublic(X509EncodedKeySpec(encodedPubKey))
        if (Arrays.equals(pubKey2.getEncoded(), encodedPubKey)) {
            println("That worked for the public key")
        }

        val privKey2 = keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedPrivKey))
        if (Arrays.equals(privKey2.getEncoded(), encodedPrivKey)) {
            println("That worked for the private key")
        }

    } catch (e: GeneralSecurityException) {
        throw IllegalStateException(e)
    }

这里 - 编码的公钥长度为 90 个字节,我想我希望它是 33 个字节

【问题讨论】:

    标签: android kotlin cryptography bouncycastle ecdsa


    【解决方案1】:

    以压缩格式对 Bouncy Castle 椭圆曲线公钥进行编码:

    Security.addProvider(BouncyCastleProvider())  
    
    generator = KeyPairGenerator.getInstance("ECDSA")
    val ecSpec = ECNamedCurveTable.getParameterSpec("prime256v1")        
    generator.initialize(ecSpec)
    val keyPair = generator.generateKeyPair()
    
    val publicKey = keyPair.public as org.bouncycastle.jce.interfaces.ECPublicKey
    val compressedPublicKey = publicKey.q.getEncoded(true)
    

    您没有包含有关如何签署密钥和对签名进行编码的所有必要细节,但我最好的猜测是使用标准编码对公钥进行标准 ECDSA 签名:

    val signer = Signature.getInstance("SHA256withECDSA")
    signer.initSign(otherPrivateKey)
    signer.update(compressedPublicKey)
    val signature = signer.sign()
    

    这使用 SHA256 对公钥进行哈希处理,使用 ECDSA 对其进行签名,并将其格式化并序列化为 ASN.1 结构 ECDSASignature 的 DER 编码。

    ECDSASignature ::= SEQUENCE {
        r   INTEGER,
        s   INTEGER
    }
    

    rs 将是正整数并使用 DER 编码。还有其他方法可以这样做,但这是迄今为止最常见的方法(唯一其他常见的 ECDSA 签名格式只是用零填充 r 和 s 并将它们连接起来)。

    【讨论】:

    • 谢谢,如果它涵盖了我在我的问题中描述的部分,您能否解释更多:签名格式 ASN.1。 ECDSA 签名的 r 和 s 值必须是正整数,并且是 DER 编码的。
    • 关于签名我将使用“SHA256withECDSA”
    • @K.Os 也许这有帮助。
    • ASN.1 / DER 签名格式与 X9.62 兼容,是 Java 的默认格式。另一种格式有时被称为“平面”签名,因为 r 和 s 值没有嵌入到层次结构中。 DER 编码的 ASN.1 INTEGER 值是有符号的,但如果设置了最高有效位,则数字将用零字节填充,因此结果始终为正。
    • @K.Os 我认为 linehrr 已经正确回答了这个问题。如果他的解决方案不起作用,请再次 ping 我。
    最近更新 更多