Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#从文本文件中的公钥获取CngKey对象_C#_.net_Cryptography - Fatal编程技术网

C#从文本文件中的公钥获取CngKey对象

C#从文本文件中的公钥获取CngKey对象,c#,.net,cryptography,C#,.net,Cryptography,我有一个文件,其中有几个ECDSA SHA256的公钥。该文件看起来像: KEY_ID: 1 STATUS: VALID -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp WVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ== -----END PUBLIC KEY----- KEY_ID: 2

我有一个文件,其中有几个ECDSA SHA256的公钥。该文件看起来像:

KEY_ID: 1
STATUS: VALID
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp
WVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ==
-----END PUBLIC KEY-----

KEY_ID: 2
STATUS: VALID
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+Y5mYZL/EEY9zGji+hrgGkeoyccK
D0/oBoSDALHc9+LXHKsxXiEV7/h6d6+fKRDb6Wtx5cMzXT9HyY+TjPeuTg==
-----END PUBLIC KEY-----

KEY_ID: 3
STATUS: VALID
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvgJ6sc2MM0AAFUJbVOD/i34YJJ8
ineqTN+DMjpI5q7fQNPEv9y2z/ecPl8qPus8flS4iLOOxdwGoF1mU9lwfA==
-----END PUBLIC KEY-----
如何为一个(或全部)键获取CngKey对象(或CngKey列表)

我试过类似的东西

string plainTextKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjpWVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ==";
byte[] publicKeyBytes = Convert.FromBase64String(plainTextKey);
CngKey ret = CngKey.Import(publicKeyBytes, CngKeyBlobFormat.EccPublicBlob);

但导入方法会抛出System.Security.Cryptography.cryptographyException作为无效参数。

我解析这样的文件已有40年了。像这样的代码已经有很长的历史了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;


namespace ConsoleApplication62
{
    enum State
    {
        FIND_KEY,
        GET_STATUS,
        GET_KEY_STRINGS
    }
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            new Key(FILENAME);

        }

    }
    public class Key
    {
        public static List<Key> keys = new List<Key>();

        public int id { get; set; }
        public Boolean status { get; set; }
        List<string> keysStrs = new List<string>();

        public Key() { }
        public Key(string filename)
        {
            StreamReader reader = new StreamReader(filename);
            string inputLine = "";

            State state = State.FIND_KEY;
            Key newKey = null;
            while((inputLine = reader.ReadLine()) != null)
            {
                inputLine = inputLine.Trim();
                if(inputLine.Length > 0)
                {
                    switch (state)
                    {
                        case State.FIND_KEY :
                            if(inputLine.StartsWith("KEY_ID:"))
                            {
                                newKey = new Key();
                                keys.Add(newKey);
                                int id = int.Parse(inputLine.Substring(inputLine.LastIndexOf(" ")));
                                newKey.id = id;
                                state = State.GET_STATUS;
                            }
                            break;

                        case State.GET_STATUS:
                            if (inputLine.StartsWith("STATUS:"))
                            {
                                string status = inputLine.Substring(inputLine.LastIndexOf(" ")).Trim();
                                newKey.status = status == "VALID" ? true : false;
                                state = State.GET_KEY_STRINGS;
                            }                            
                            break;

                        case State.GET_KEY_STRINGS:
                            if (!inputLine.StartsWith("-"))
                            {
                                newKey.keysStrs.Add(inputLine.Trim());
                            }
                            else
                            {
                                if (inputLine.Contains("END PUBLIC KEY"))
                                {
                                    state = State.FIND_KEY;
                                }
                            }
                            break;
                    }
                }

            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.IO;
命名空间控制台应用程序62
{
枚举状态
{
找到你的钥匙,
获得你的地位,
获取\u键\u字符串
}
班级计划
{
常量字符串文件名=@“c:\temp\test.txt”;
静态void Main(字符串[]参数)
{
新密钥(文件名);
}
}
公开类密钥
{
公共静态列表键=新列表();
公共int id{get;set;}
公共布尔状态{get;set;}
List KEYSTRS=新列表();
公钥(){}
公钥(字符串文件名)
{
StreamReader=新的StreamReader(文件名);
字符串inputLine=“”;
State State=State.FIND_键;
Key newKey=null;
而((inputLine=reader.ReadLine())!=null)
{
inputLine=inputLine.Trim();
如果(inputLine.Length>0)
{
开关(状态)
{
case State.FIND_键:
if(inputLine.StartsWith(“KEY\u ID:”)
{
newKey=newKey();
keys.Add(newKey);
int id=int.Parse(inputLine.Substring(inputLine.LastIndexOf(“”));
newKey.id=id;
state=state.GET_STATUS;
}
打破
case State.GET_状态:
if(inputLine.StartsWith(“状态:))
{
字符串状态=inputLine.Substring(inputLine.LastIndexOf(“”).Trim();
newKey.status=状态==“有效”?真:假;
state=state.GET\u KEY\u字符串;
}                            
打破
case State.GET_KEY_字符串:
如果(!inputLine.StartsWith(“-”)
{
newKey.keystrs.Add(inputLine.Trim());
}
其他的
{
if(inputLine.Contains(“结束公钥”))
{
state=state.FIND_键;
}
}
打破
}
}
}
}
}
}

EccPublicBlob
映射到格式类型,而不是X.509 SubjectPublicKeyInfo

如果您的所有密钥都在secp256r1/NIST p-256上,那么有一种非常简单的黑客方法

您可能已经注意到,您的所有键都以
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
开头。我们很快就会知道原因

皈依

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp
WVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ==
到字节(此处为十六进制):

这是一个DER编码的blob

用我们的德福,我们看到

// SubjectPublicKeyInfo
30 59  // SEQUENCE, 0x59 == 89 bytes of payload
   // AlgorithmIdentifier
   30 13  // SEQUENCE, 0x13 == 19 bytes of payload
      // AlgorithmIdentifier.algorithm
      06 07 2A 86 48 CE 3D 02 01  // OBJECT ID 1.2.840.10045.2.1 (id-ecPublicKey)
      // AlgorithmIdentifier.parameters
      06 08 2A 86 48 CE 3D 03 01 07 // OBJECT ID 1.2.840.10045.3.1.7 (secp256r1)
   // SubjectPublicKeyInfo.publicKey
   03 42 00  // BIT STRING, 0x42 == 66 (65) payload bytes, 0 unused bits
      // "the public key"
      04
      92F809EAC73630CD000055096D5383FE2DF860927C8A77AA4CDF83323A48E6AE
      DF40D3C4BFDCB6CFF79C3E5F2A3EEB3C7E54B888B38EC5DC06A05D6653D9707C
由于算法标识符是
id ecPublicKey
,因此参数是识别曲线的OID(在本例中为secp256r1/NIST p-256)。“公钥”的格式为(2.3.4八进制字符串到椭圆曲线点转换)

最常见的编码是类型
04
,未压缩密钥。(0x04,后跟填充至必要长度的Qx,后跟填充至必要长度的Qy)

因此,对于secp256r1上使用类型04编码的所有点,字节模式以

30 59 30 13 06 07 2A 86 48 CE 3D 02 01 06 08 2A 
86 48 CE 3D 03 01 07 03 42 00 04
它恰好与MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE的公共base64前缀对齐

什么是[32位标识符][32位小尾端长度][padded Qx][padded Qy]

因此,super duper hacky版本是:

private static readonly byte[] s_secp256r1Prefix =
    Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");

// For ECDH instead of ECDSA, change 0x53 to 0x4B.
private static readonly byte[] s_cngBlobPrefix = { 0x45, 0x43, 0x53, 0x31, 0x20, 0, 0, 0 };

private static CngKey ImportECDsa256PublicKey(string base64)
{
    byte[] subjectPublicKeyInfo = Convert.FromBase64String(base64);

    if (subjectPublicKeyInfo.Length != 91)
        throw new InvalidOperationException();

    byte[] prefix = s_secp256r1Prefix;

    if (!subjectPublicKeyInfo.Take(prefix.Length).SequenceEqual(prefix))
        throw new InvalidOperationException();

    byte[] cngBlob = new byte[s_cngBlobPrefix.Length + 64];
    Buffer.BlockCopy(s_cngBlobPrefix, 0, cngBlob, 0, s_cngBlobPrefix.Length);

    Buffer.BlockCopy(
        subjectPublicKeyInfo,
        s_secp256r1Prefix.Length,
        cngBlob,
        s_cngBlobPrefix.Length,
        64);

    return CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob);
}
为了支持其他曲线,您需要将CNG blob的前4个字节更改为正确的“Magic”值,并将第5个字节更改为正确的长度。当然,还有不同的SubjectPublicKeyInfo前缀,64不是公钥坐标长度(64==256/8*2)。但所有这些都留给读者作为练习


请参阅以获取相反信息。

我知道如何使用密钥解析文件,但我无法从解析的明文公钥获取CngKey对象。您确定使用了正确的设置,包括SHA256吗?我以前在decrypt/encrpt中也看到过类似的错误。上次我遇到问题时,有5个不同的参数设置,所以我编写了一个5嵌套for循环,并尝试了所有选项,直到找到正确的选项。在循环中放置一些异常处理程序,以便在发生异常时代码继续。我找到了正确的属性组合。文档有时很模糊。多全面的回答啊。拍我的手。
private static readonly byte[] s_secp256r1Prefix =
    Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");

// For ECDH instead of ECDSA, change 0x53 to 0x4B.
private static readonly byte[] s_cngBlobPrefix = { 0x45, 0x43, 0x53, 0x31, 0x20, 0, 0, 0 };

private static CngKey ImportECDsa256PublicKey(string base64)
{
    byte[] subjectPublicKeyInfo = Convert.FromBase64String(base64);

    if (subjectPublicKeyInfo.Length != 91)
        throw new InvalidOperationException();

    byte[] prefix = s_secp256r1Prefix;

    if (!subjectPublicKeyInfo.Take(prefix.Length).SequenceEqual(prefix))
        throw new InvalidOperationException();

    byte[] cngBlob = new byte[s_cngBlobPrefix.Length + 64];
    Buffer.BlockCopy(s_cngBlobPrefix, 0, cngBlob, 0, s_cngBlobPrefix.Length);

    Buffer.BlockCopy(
        subjectPublicKeyInfo,
        s_secp256r1Prefix.Length,
        cngBlob,
        s_cngBlobPrefix.Length,
        64);

    return CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob);
}