当前位置: 首页 > 知识库问答 >
问题:

将. NET ECDSA P1363签名转换为充气城堡ASN.1签名

寇和璧
2023-03-14

我正在尝试使用.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();

由于某些原因,bcRmsR已经不同,我假设问题甚至可能在转换之前就出现了。我已经看过许多StackOverflow文章(例如,使用Crypto?将签名从P1363转换为ASN.1/DER格式,用C#中的Bouncy Castle验证ECDSA签名,用C#中的Boundy Castle检验ECDSA签字),但似乎做得不对。任何提示都将不胜感激!

共有1个答案

邢凌
2023-03-14

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

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

在 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();

        }
    }

使用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)
    }
}

希望它至少能帮助有类似问题的人。

 类似资料:
  • 我的项目正在对来自某些第三方软件的某些数据集进行签名验证。使用的签名算法是 。当我使用SDK附带的标准SUN加密提供程序时,一切都很顺利。最近我切换到了Bouncy Castle 1.50,之后,一些以前(即SUN提供者)进行验证的数据集开始失败,而其余的仍然被验证正常。 我探索了两个提供程序的源代码,结果发现SDK的默认提供程序对格式错误的签名有某种保护(同时能够恢复),而Bouncy Cast

  • 我需要用Java中的Bouncy Castle创建一个自签名X509证书,但我尝试包含的每个类都不推荐使用。我该怎么解决这个问题?还有其他课程吗?谢谢

  • 我正在开发一个功能来对某些内容进行数字签名。我有一个带有私钥的有效证书。如何使用私钥和充气城堡进行数字签名? 我尝试了以下方法,但想要一些正确的方法来实现同样的使用充气城堡: 谢谢!

  • 有人能解释一下为什么这段代码在解密密钥时会在最后一行抛出吗? 以下是来自https://stackoverflow.com/a/27886397/66722对于使用OAEP的RSA也是如此? “RSA/ECB/PKCS1Padding”实际上没有实现ECB模式加密。它应该被称为“RSA/None/PKCS1Padding”,因为它只能用于加密单个明文块(或者实际上是一个密钥)。这只是Sun/Ora

  • 首先,我需要在C#代码中复制功能。我设法将私钥加载到并调用,但得到的结果与不同。 据我所知,根据这篇文章和这个RFC,当被调用时,没有,openssl不会用digest函数标识符捆绑数据,并跳过ASN.1编码。这意味着对原始数据进行填充和加密。这个工具似乎确认,解密签名看起来像填充的明文数据。 那么用C#怎么做呢?中的所有函数都要求在加密之前指定哈希算法和编码签名。我是否遗漏了什么,或者我将不得不

  • 问题内容: 如何使用MapKit从坐标获取地址? 当在地图上长按时,我得到了以下代码: 我想用完整的地址(街道,城市,邮政编码,国家)打印。 问题答案: SWIFT 4.2:编辑 框架确实提供了一种从坐标获取地址详细信息的方法。 您需要使用地图套件的 反向地理编码 。类用于从地址获取位置,并从位置(坐标)获取地址。该方法将从坐标返回地址详细信息。 此方法接受作为参数并返回,其中包含地址字典。 所以