【问题标题】:Converting a .NET ECDSA P1363 signature to a Bouncy Castle ASN.1 Signature将 .NET ECDSA P1363 签名转换为 Bouncy Castle ASN.1 签名
【发布时间】:2019-12-08 13:45:44
【问题描述】:

我正在尝试使用来自 .NET(CNG 提供商)的现有 ECDSA 密钥,以便使用 Bouncy Castle 对数据进行签名,然后在两种签名格式(P1363 和 ASN.1)之间进行转换。但是,我总是得到不同的签名。我不太确定转换是否不正确,或者我是否使用了错误的方法来使用 Bouncy Castle 读取 .NET 密钥。

        var ecdsaKey = new ECDsaCng(cngKey);    
        var testData = Encoding.UTF8.GetBytes("test");
        var akPrivate = PrivateKeyFactory.CreateKey(cngKey.Export(CngKeyBlobFormat.Pkcs8PrivateBlob));
        var bcSigner = SignerUtilities.GetSigner("SHA256withECDSA");
        bcSigner.Init(true, akPrivate);
        bcSigner.BlockUpdate(testData, 0, testData.Length);
        var bcSignature = bcSigner.GenerateSignature();
        var asn1Stream = new Asn1InputStream(bcSignature);

        var bcDerSequence = ((DerSequence) asn1Stream.ReadObject());
        var bcR = ((DerInteger) bcDerSequence[0]).PositiveValue;
        var bcS = ((DerInteger) bcDerSequence[1]).PositiveValue;

        var msSignature = ecdsaKey.SignData(testData, HashAlgorithmName.SHA256);

        // P1363 to ASN.1
        var msR = new BigInteger(1, msSignature.Take(msSignature.Length/2).ToArray());
        var msS = new BigInteger(1, msSignature.Skip(msSignature.Length/2).ToArray());
        var msRDer = new DerInteger(msR);
        var msSDer = new DerInteger(msS);
        var msDerSequence = new DerSequence(msRDer, msSDer).GetDerEncoded();

由于某种原因bcR 和msR 已经不同,我认为问题可能甚至在转换之前。我已经看过很多 StackOverflow 文章(例如Convert signature from P1363 to ASN.1/DER format using Crypto++?、Verifying ECDSA signature with Bouncy Castle in C#、Verifying ECDSA signature with Bouncy Castle in C#),但似乎无法正确理解。任何提示将不胜感激!

【问题讨论】:

  • 你有解决这个@terminal的办法吗?
  • @AdityaSharma 不幸的是,不完全是。我最终为我的特定用例采用了不同的方法,我发布了它以供参考。

标签: c# bouncycastle ecdsa cng


【解决方案1】:

很遗憾,我从未解决过这个问题。但是,我为我的特定用例提出了一种不同的方法。我的用例是读取 Kotlin 中的 Pkcs8PrivateBlob .NET 密钥并生成/验证与 .NET 中生成的签名兼容的签名(即 .NET 中生成的签名应该在 Kotlin 中有效,反之亦然)。我在这里发布我的方法以供参考,但请注意它不是原始问题的解决方案。它也不是一个非常优雅的解决方案,很可能不是最简单的解决方案,但我必须以某种方式让它工作。

较新的 JDK 版本支持 P1363 签名格式,这意味着至少我只需要将密钥 blob 转换为 Java 可读的格式,即 PKCS#8。尽管如此,我还是使用 BouncyCastle 在 Kotlin 中创建了初始密钥对。

在 C# 中将私钥 blob 转换为 PKCS#8

    internal enum KeyBlobMagicNumber : int
    {
        BCRYPT_DSA_PUBLIC_MAGIC = 0x42505344,
        BCRYPT_DSA_PRIVATE_MAGIC = 0x56505344,
        BCRYPT_DSA_PUBLIC_MAGIC_V2 = 0x32425044,
        BCRYPT_DSA_PRIVATE_MAGIC_V2 = 0x32565044,

        BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345,
        BCRYPT_ECDH_PRIVATE_P256_MAGIC = 0x324B4345,
        BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345,
        BCRYPT_ECDH_PRIVATE_P384_MAGIC = 0x344B4345,
        BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345,
        BCRYPT_ECDH_PRIVATE_P521_MAGIC = 0x364B4345,
        BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC = 0x504B4345,
        BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC = 0x564B4345,

        BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345,
        BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345,
        BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345,
        BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345,
        BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345,
        BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345,
        BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC = 0x50444345,
        BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC = 0x56444345,

        BCRYPT_RSAPUBLIC_MAGIC = 0x31415352,
        BCRYPT_RSAPRIVATE_MAGIC = 0x32415352,
        BCRYPT_RSAFULLPRIVATE_MAGIC = 0x33415352,
        BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b,
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct BCRYPT_ECCKEY_BLOB
    {
        internal KeyBlobMagicNumber Magic;
        internal int cbKey;
    }


    // ===== THE CONVERSION PROCESS =====


    byte[] d;

    byte[] blobBytes = // store Pkcs8PrivateBlob here

    // Parse the binary blob into a BCRYPT_ECCKEY_BLOB structure
    // (see https://docs.microsoft.com/en-gb/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_ecckey_blob?redirectedfrom=MSDN)
    unsafe
    {
        fixed (byte* pBlob = blobBytes)
        {
            BCRYPT_ECCKEY_BLOB* pBcryptBlob = (BCRYPT_ECCKEY_BLOB*) pBlob;

            var headerSize = sizeof(BCRYPT_ECCKEY_BLOB);
            var keyData = blobBytes.Skip(headerSize).ToArray();

            d = keyData.Skip(pBcryptBlob->cbKey * 2).ToArray();

            // The values in the blob are big-endian. Check if we need to reverse them to little-endian
            if (BitConverter.IsLittleEndian)
            {
                d = Arrays.Reverse(d);
            }

            // Append 0 to mark the data as unsigned as per the documentation of System.Numerics.BigInteger
            var unsignedByteData = d.Concat(new byte[] {0}).ToArray();
            var dValue = new System.Numerics.BigInteger(unsignedByteData);

            // Create a BouncyCastle BigInteger from the string representation to make sure that we use the
            // same endianess and sign
            var dValueBC = new BigInteger(dValue.ToString());
            var ecDomainParameters = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.secp384r1);

            // Build a private key from the parsed d value
            var privateKey = new ECPrivateKeyParameters(dValueBC, ecDomainParameters);

            // Export the private key in PKCS#8 format
            var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);

            var result = privateKeyInfo.GetDerEncoded();

        }
    }

在 Kotlin 中使用 BouncyCastle 从 PKCS#8 私钥创建密钥对

class KeyConverter : IKeyConverter {
    init {
        Security.addProvider(BouncyCastleProvider())
    }

    override fun getKeyPair(pkcs8Base64Key: String): KeyPair {
        val decoder = Base64.getDecoder()
        val keyBytes = decoder.decode(pkcs8Base64Key)
        val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(keyBytes)

        val defaultKeyFactory = KeyFactory.getInstance("EC", "BC")
        val privateKey = defaultKeyFactory.generatePrivate(pkcS8EncodedKeySpec) as ECPrivateKey
        val publicKey = publicKeyFromPrivateKey(privateKey)
        return KeyPair(publicKey, privateKey)
    }

    private fun publicKeyFromPrivateKey(key: ECPrivateKey): PublicKey {
        val bcKeyFactory = KeyFactory.getInstance("EC", "BC")
        val ecParameterSpec = ECNamedCurveTable.getParameterSpec("secp384r1")
        val Q = ecParameterSpec.g.multiply(JCEECPrivateKey(key).d)
        val publicKeySpec = ECPublicKeySpec(Q, ecParameterSpec)
        return bcKeyFactory.generatePublic(publicKeySpec)
    }
}

在 Kotlin 中使用密钥对创建/验证 P1363 签名

class DefaultCryptoProvider(private val keyPair: KeyPair) {

    private val randomSource: SecureRandom = SecureRandom()

    override fun signData(data: ByteArray): ByteArray {
        val signature = Signature.getInstance("SHA256withECDSAinP1363Format")
        signature.initSign(keyPair.private)
        signature.update(data)
        return signature.sign()
    }

    override fun verifyData(data: ByteArray, signatureData: ByteArray): Boolean {
        val signature = Signature.getInstance("SHA256withECDSAinP1363Format")
        signature.initVerify(keyPair.public)
        signature.update(data)
        return signature.verify(signatureData)
    }
}

希望它至少可以帮助遇到类似问题的人。

【讨论】:

    猜你喜欢
    • 2021-07-30
    • 2020-01-25
    • 2022-11-04
    • 2012-06-20
    • 1970-01-01
    • 2016-10-01
    • 2020-11-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多