首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将公钥从其他地方导入到CngKey?

将公钥从其他地方导入到CngKey?
EN

Stack Overflow用户
提问于 2014-06-16 20:05:14
回答 4查看 11.7K关注 0票数 19

我正在寻找一种跨平台的方式来共享ECDSA签名的公钥。从性能的角度来看,从CngKey和标准.NET密码库的角度来看,我做了一件很棒的事情,但是我不知道一个33 (或65)字节的公钥(使用secp256r1/P256)是如何被MS转换成104个字节的。所以,我不能支持跨平台签名和验证..。

我现在正在使用BouncyCastle,但是神圣的手杖慢了!

因此,为下列要求寻找建议:

  1. 跨平台/跨语言(服务器为.NET,但这是通过JSON/Web.API接口提供的)
    • JavaScript,Ruby,Python,C++等。

  1. 在服务器上没有那么慢的疯狂
  2. 没那么慢的人不能用在客户身上。

客户端必须能够对消息进行签名,服务器必须能够使用注册服务时交换的公钥验证签名。

不管怎样,想法会很棒的..。谢谢

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-06-17 03:00:50

因此,我已经计算出了CngKey在ECCPublicKeyBlob和ECCPrivateKeyBlob中导出的格式。这应该允许其他人在其他键格式和椭圆曲线签名的CngKey之间进行互操作等等。

ECCPrivateKeyBlob被格式化(用于P256)如下

  • 密钥类型(4字节)公钥(64字节)
  • HEX的关键类型是45-43-53-32。
  • HEX键长为20-00-00-00 -00。
  • 公钥是未压缩格式减去前导字节(在其他库中表示未压缩密钥的总是04 )。

ECCPublicKeyBlob被格式化(用于P256)如下

  • 密钥类型(4字节)公钥(64字节)
  • HEX的关键类型是45-43-53-31。
  • HEX键长为20-00-00-00 -00。
  • 公钥是未压缩格式减去前导字节(在其他库中表示未压缩密钥的总是04 )。

因此,给出另一种语言中未压缩的十六进制公钥,您可以修剪第一个字节,将这8个字节添加到前面,然后使用

代码语言:javascript
复制
CngKey.Import(key,CngKeyBlobFormat.EccPrivateBlob);

备注:关键的blob格式是由微软记录的。

键类型和密钥长度在水滴结构中定义为:

代码语言:javascript
复制
{ ulong Magic; ulong cbKey; }

ECC公钥存储格式:

代码语言:javascript
复制
BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.

ECC私钥内存格式:

代码语言:javascript
复制
BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.
BYTE d[cbKey] // Big-endian.

.NET中可用的神奇值在微软官方的GitHub dotnet/corefx /Interop.BLOBS中。

代码语言:javascript
复制
internal enum KeyBlobMagicNumber : int
{
    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_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,
    ...
    ...
}
票数 38
EN

Stack Overflow用户

发布于 2015-10-23 18:58:56

感谢您,我能够使用以下代码从证书中导入ECDSA_P256公钥:

代码语言:javascript
复制
    private static CngKey ImportCngKeyFromCertificate(X509Certificate2 cert)
    {
        var keyType = new byte[] {0x45, 0x43, 0x53, 0x31};
        var keyLength = new byte[] {0x20, 0x00, 0x00, 0x00};

        var key = cert.PublicKey.EncodedKeyValue.RawData.Skip(1);

        var keyImport = keyType.Concat(keyLength).Concat(key).ToArray();

        var cngKey = CngKey.Import(keyImport, CngKeyBlobFormat.EccPublicBlob);
        return cngKey;
    }

65字节密钥(仅公开密钥)以需要删除的0x04开始。然后添加您描述的标题。

然后我就能核实出这样的签名:

代码语言:javascript
复制
var crypto = ECDsaCng(cngKey);
var verify = crypto.VerifyHash(hash, sig);
票数 5
EN

Stack Overflow用户

发布于 2016-10-14 18:33:02

我只是想感谢以上两篇文章,因为这极大地帮助了我。我必须使用RSACng对象使用RSA公钥验证签名。我以前使用过RSACryptoServiceProvider,但这不是FIPS兼容的,所以我在切换到RSACng时遇到了一些问题。它还需要.NET 4.6。下面是以上面的海报为例,让它工作的方法:

代码语言:javascript
复制
                    // This structure is as the header for the CngKey
                    // all should be byte arrays in Big-Endian order
                    //typedef struct _BCRYPT_RSAKEY_BLOB {
                    //  ULONG Magic; 
                    //  ULONG BitLength; 
                    //  ULONG cbPublicExp;
                    //  ULONG cbModulus;
                    //  ULONG cbPrime1;  private key only
                    //  ULONG cbPrime2;  private key only
                    //} BCRYPT_RSAKEY_BLOB;

                    // This is the actual Key Data that is attached to the header
                    //BCRYPT_RSAKEY_BLOB
                    //  PublicExponent[cbPublicExp] 
                    //  Modulus[cbModulus]

                    //first get the public key from the cert (modulus and exponent)
                    // not shown
                    byte[] publicExponent = <your public key exponent>; //Typically equal to from what I've found: {0x01, 0x00, 0x01}
                    byte[] btMod = <your public key modulus>;  //for 128 bytes for 1024 bit key, and 256 bytes for 2048 keys

                    //BCRYPT_RSAPUBLIC_MAGIC = 0x31415352,
                    // flip to big-endian
                    byte[] Magic = new byte[] { 0x52, 0x53, 0x41, 0x31}; 

                    // for BitLendth: convert the length of the key's Modulus as a byte array into bits,
                    // so the size of the key, in bits should be btMod.Length * 8. Convert to a DWord, then flip for Big-Endian 
                    // example 128 bytes = 1024 bits = 0x00000400 = {0x00, 0x00, 0x04, 0x00} = flipped {0x00, 0x04, 0x00, 0x00}
                    // example 256 bytes = 2048 bits = 0x00000800 = {0x00, 0x00, 0x08, 0x00} = flipped {0x00, 0x08, 0x00, 0x00}
                    string sHex = (btMod.Length * 8).ToString("X8");
                    byte[] BitLength = Util.ConvertHexStringToByteArray(sHex);
                    Array.Reverse(BitLength); //flip to Big-Endian

                    // same thing for exponent length (in bytes)
                    sHex = (publicExponent.Length).ToString("X8");
                    byte[] cbPublicExp = Util.ConvertHexStringToByteArray(sHex);
                    Array.Reverse(cbPublicExp);

                    // same thing for modulus length (in bytes)
                    sHex = (btMod.Length).ToString("X8");
                    byte[] cbModulus = Util.ConvertHexStringToByteArray(sHex);
                    Array.Reverse(cbModulus);                      

                    // add the 0 bytes for cbPrime1 and cbPrime2 (always zeros for public keys, these are used for private keys, but need to be zero here)
                    // just make one array with both 4 byte primes as zeros
                    byte[] cbPrimes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

                    //combine all the parts together into the one big byte array in the order the structure
                    var keyImport = Magic.Concat(BitLength).Concat(cbPublicExp).Concat(cbModulus).Concat(cbPrimes).Concat(publicExponent).Concat(btMod).ToArray();

                    var cngKey = CngKey.Import(keyImport, CngKeyBlobFormat.GenericPublicBlob);

                    // pass the key to the class constructor
                    RSACng rsa = new RSACng(cngKey);

                    //verify: our randomly generated M (message) used to create the signature (not shown), the signature, enum for SHA256, padding
                    verified = rsa.VerifyData(M, signature, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);

注意:模数(0x00)的符号字节既可以包含在模数中,也可以不包括在模数中,所以如果包含它,长度就会更大。不管怎样,CNGkey似乎都处理得很好。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24251336

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档