如何从字节数组指定Bouncy Castle公钥?

huangapple go评论83阅读模式
英文:

How do I specify a Bouncy Castle public key from a Byte array?

问题

我需要通过I2C接口与微控制器实现密钥交换,密钥以字节形式传输。我正在使用Visual Studio 2022和C#进行开发。由于我需要访问秘密协议(ECDH),所以无法使用Visual Studio加密库,因此我转向了Bouncy Castle。我能够将公钥的两个部分存储为字节数组以发送到微控制器 - 示例代码如下 - 但是我无法弄清楚如何将从微控制器接收到的公钥放入Bouncy Castle的公钥对象中,以便在生成秘密协议时使用。

public void TestBC()
{
    String SecretString = "";
    int j;

    AsymmetricKeyPair<AsymmetricECPublicKey, AsymmetricECPrivateKey> InitiatorKey = GenerateECDHKeyPair();
    AsymmetricKeyPair<AsymmetricECPublicKey, AsymmetricECPrivateKey> RecipientKey = GenerateECDHKeyPair();
    String RecipPublicKeyX = RecipientKey.PublicKey.W.XCoord.ToString();
    String RecipPublicKeyY = RecipientKey.PublicKey.W.YCoord.ToString();
    MessageBox.Show(RecipPublicKeyW + "\r\n" + RecipPublicKeyX + "\r\n" + RecipPublicKeyY);
    Byte[] SecretAgreement = GetSecretAgreement(InitiatorKey.PrivateKey, RecipientKey.PublicKey);
    Byte[] publicX = ConvertHexStringToByteArray(RecipPublicKeyX);
    Byte[] publicY = ConvertHexStringToByteArray(RecipPublicKeyY);

    for(j=0; j<SecretAgreement.Length; j++)
        SecretString += String.Format("{0:X2}",SecretAgreement[j]);
    MessageBox.Show(SecretString);
}

public AsymmetricKeyPair<AsymmetricECPublicKey, AsymmetricECPrivateKey> GenerateECDHKeyPair()
{
    FipsEC.KeyGenerationParameters KeyGenParameters = new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P256).For(FipsEC.Cdh);
    FipsEC.KeyPairGenerator KpGen = CryptoServicesRegistrar.CreateGenerator(KeyGenParameters, new Org.BouncyCastle.Security.SecureRandom());
    return KpGen.GenerateKeyPair();
}

public static byte[] GetSecretAgreement(AsymmetricECPrivateKey InitiatorPrivate, AsymmetricECPublicKey RecipientPublic)
{
    IAgreementCalculatorService agreeFact = CryptoServicesRegistrar.CreateService(InitiatorPrivate);
    IAgreementCalculator<FipsEC.AgreementParameters> agreement = agreeFact.CreateAgreementCalculator(FipsEC.Cdh);
    return agreement.Calculate(RecipientPublic);
}

public static byte[] ConvertHexStringToByteArray(string hexString)
{
    if (hexString.Length % 2 != 0)
    {
        throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The hex string cannot have an odd number of digits: {0}", hexString));
    }

    byte[] data = new byte[hexString.Length / 2];
    for (int index = 0; index < data.Length; index++)
    {
        string byteValue = hexString.Substring(index * 2, 2);
        data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
    }

    return data;
}

Visual Studio的智能感知不显示AsymmetricECPublicKey对象的任何构造函数、属性或方法,允许我将公钥从字节数组(或字符串)中输入。而且协议计算器似乎需要一个公钥对象。我找到了一些类似的问题已经发布,但它们没有帮助我。有没有人知道如何使用字节数组或字符串来实现这个目标?

英文:

I need to implement key exchange through an I2C interface with a microcontoller, with the keys transferred byte by byte. I am working in Visual Studio 2022, C#. Because I need access to the Secret Agreement (ECDH), I can't use the Visual Studio encryption library, so have turned to Bouncy Castle. I am able to get the two parts of the public key into Byte arrays to send to the micro - example code shown - but have not been able to figure out how to put the public key received from the micro into a Bouncy Castle public key object to use in generating a secret agreement.

        public void TestBC()
        {
            String SecretString = &quot;&quot;;
            int j;

            AsymmetricKeyPair&lt;AsymmetricECPublicKey, AsymmetricECPrivateKey&gt; InitiatorKey = GenerateECDHKeyPair();
            AsymmetricKeyPair&lt;AsymmetricECPublicKey, AsymmetricECPrivateKey&gt; RecipientKey = GenerateECDHKeyPair();
            String RecipPublicKeyX = RecipientKey.PublicKey.W.XCoord.ToString();
            String RecipPublicKeyY = RecipientKey.PublicKey.W.YCoord.ToString();
            MessageBox.Show(RecipPublicKeyW + &quot;\r\n&quot; + RecipPublicKeyX + &quot;\r\n&quot; + RecipPublicKeyY);
            Byte[] SecretAgreement = GetSecretAgreement(InitiatorKey.PrivateKey, RecipientKey.PublicKey);
            Byte[] publicX = ConvertHexStringToByteArray(RecipPublicKeyX);
            Byte[] publicY = ConvertHexStringToByteArray(RecipPublicKeyY);

            for(j=0; j&lt;SecretAgreement.Length; j++)
                SecretString += String.Format(&quot;{0:X2}&quot;,SecretAgreement[j]);
            MessageBox.Show(SecretString);
        }

        public AsymmetricKeyPair&lt;AsymmetricECPublicKey, AsymmetricECPrivateKey&gt; GenerateECDHKeyPair()
        {
            FipsEC.KeyGenerationParameters KeyGenParameters = new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P256).For(FipsEC.Cdh);
            FipsEC.KeyPairGenerator KpGen = CryptoServicesRegistrar.CreateGenerator(KeyGenParameters, new Org.BouncyCastle.Security.SecureRandom());
            return KpGen.GenerateKeyPair();
        }

        public static byte[] GetSecretAgreement(AsymmetricECPrivateKey InitiatorPrivate, AsymmetricECPublicKey RecipientPublic)
        {
            IAgreementCalculatorService agreeFact = CryptoServicesRegistrar.CreateService(InitiatorPrivate);
            IAgreementCalculator&lt;FipsEC.AgreementParameters&gt; agreement = agreeFact.CreateAgreementCalculator(FipsEC.Cdh);
            return agreement.Calculate(RecipientPublic);
        }

        public static byte[] ConvertHexStringToByteArray(string hexString)
        {
            if (hexString.Length % 2 != 0)
            {
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, &quot;The hex string cannot have an odd number of digits: {0}&quot;, hexString));
            }

            byte[] data = new byte[hexString.Length / 2];
            for (int index = 0; index &lt; data.Length; index++)
            {
                string byteValue = hexString.Substring(index * 2, 2);
                data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
            }

            return data;
        }

Visual Studio intellisense does not show any constructors, properties, or methods for the AsymmetricECPublicKey object that will allow me to put in the pbulic key from a Byte array (or string). And the agreement calculator appears to require a public key object. I found a couple of similar questions already posted, but they did not help me. Does anyone know how to use a Byte array or string to do this?

答案1

得分: 0

如果给定公钥的原始 x 和 y 坐标,首先应将其转换为未压缩格式。未压缩格式由以下内容串联而成:一个前导的 0x04 字节,x 坐标和 y 坐标。对于 P-256,x 和 y 坐标都是 32 字节(如果 x 坐标较小,必须从前面用 0x00 填充为 32 字节;对 y 坐标也适用相同的规则):

Byte[] publicX = ...;
Byte[] publicY = ...;
Byte[] uncompressedKey = new byte[1 + 32 + 32];  // 对太短的值进行填充(即小于 32 字节的值),填充到前面用 0x00
uncompressedKey[0] = 0x04;
Buffer.BlockCopy(publicX, 0, uncompressedKey, 1 + (32 - publicX.Length), publicX.Length);          
Buffer.BlockCopy(publicY, 0, uncompressedKey, 1 + 32 + (32 - publicY.Length), publicY.Length);

然后,可以按以下方式导入未压缩密钥:

FipsEC.KeyGenerationParameters keyGenParameters = new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P256).For(FipsEC.Cdh);
AsymmetricECPublicKey publicKey = new AsymmetricECPublicKey(FipsEC.Alg, KeyGenParameters.DomainParameters, KeyGenParameters.DomainParameters.Curve.DecodePoint(uncompressedKey));

为了完整起见:也可以将密钥转换为压缩格式并导入。压缩格式是一个前导的 0x02 字节(如果 y 是偶数)或 0x03 字节(如果 y 是奇数),以及 x 坐标的串联(根据此信息,可以唯一确定公钥,因为可以从此信息重建出 y 坐标)。

英文:

Preliminary note: The question and answer refer to the C#/BouncyCastle FIPS version and not the regular C#/BouncyCastle version.

If the public key is given as raw x and y coordinates, it should first be converted to the uncompressed format. The uncompressed format is given by the concatenation of a leading 0x04 byte, the x coordinate and the y coordinate. The last two are 32 bytes each for P-256 (if the x coordinate is smaller, it must be padded from the front with 0x00 to 32 bytes; the same applies to the y coordinate):

Byte[] publicX = ...;
Byte[] publicY = ...;
Byte[] uncompressedKey = new byte[1 + 32 + 32];  // pad too short values (i.e. values &lt; 32 bytes) with leading 0x00
uncompressedKey[0] = 0x04;
Buffer.BlockCopy(publicX, 0, uncompressedKey, 1 + (32 - publicX.Length), publicX.Length);          
Buffer.BlockCopy(publicY, 0, uncompressedKey, 1 + 32 + (32 - publicY.Length), publicY.Length);

The uncompressed key can then be imported as follows:

FipsEC.KeyGenerationParameters keyGenParameters = new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P256).For(FipsEC.Cdh);
AsymmetricECPublicKey publicKey = new AsymmetricECPublicKey(FipsEC.Alg, KeyGenParameters.DomainParameters, KeyGenParameters.DomainParameters.Curve.DecodePoint(uncompressedKey));

For completeness: It is also possible to convert the key to the compressed format and import it. The compressed format is the concatenation of a leading 0x02 byte (if y is even) or 0x03 byte (if y is odd) and the x coordinate (with this information the public key is uniquely determined, since from this information the y coordinate can be reconstructed).

huangapple
  • 本文由 发表于 2023年7月10日 21:33:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76654275.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定