C# 在联机编译器中运行的C ECDSA签名失败

C# 在联机编译器中运行的C ECDSA签名失败,c#,sign,ecdsa,online-compilation,C#,Sign,Ecdsa,Online Compilation,我正在Windows计算机Win 10 x64上成功运行此代码,运行的是dotnet 4.7.2。它生成EC密钥对P-256,使用SHA-256散列明文,使用EC私钥对散列进行签名,并使用EC公钥根据散列明文验证签名 我得到了这个输出,所以一切正常: EC signature curve secp256r1 / P-256 string dataToSign: The quick brown fox jumps over the lazy dog * * * sign the plaintext

我正在Windows计算机Win 10 x64上成功运行此代码,运行的是dotnet 4.7.2。它生成EC密钥对P-256,使用SHA-256散列明文,使用EC私钥对散列进行签名,并使用EC公钥根据散列明文验证签名

我得到了这个输出,所以一切正常:

EC signature curve secp256r1 / P-256 string
dataToSign: The quick brown fox jumps over the lazy dog
* * * sign the plaintext with the EC private key * * *
EC keysize: 256
signature (Base64): cwLBRSt1vtO33tHWcTdx1OTu9lBFXHEJgvdRyDUynLLE5eMakUZUAKLwaJvYoS7NBylx2Zz0+G6dvgJ6xv5qNA==
* * *verify the signature against hash of plaintext with the EC public key * * *
signature verified: True
现在我试图找到任何能够运行代码的在线编译器。我最喜欢的编译器 ,Mono C编译器版本6.8.0.123,完整代码:遇到以下错误:

Unhandled Exception:
System.NotImplementedException: The method or operation is not implemented.
  at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.NotImplementedException: The method or operation is not implemented.
  at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
exit status 1
所以我的问题是:是否有任何在线编译器能够运行代码

我想我的问题可能是一个小话题,所以——在本例中——是否有其他stackexchange站点更适合我的问题

警告:以下代码没有异常处理,仅用于教育目的:

using System;
using System.Security.Cryptography;

class EcSignatureString {
    static void Main() {

    Console.WriteLine("EC signature curve secp256r1 / P-256 string");
    string dataToSignString = "The quick brown fox jumps over the lazy dog";
    byte[] dataToSign = System.Text.Encoding.UTF8.GetBytes(dataToSignString);
    Console.WriteLine("dataToSign: " + dataToSignString);
    try {
        Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");

        ECDsaCng ecDsaKeypair = new ECDsaCng(256);
        Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);

        byte[] hashedData = null;
        byte[] signature = null;
        // create new instance of SHA256 hash algorithm to compute hash
        HashAlgorithm hashAlgo = new SHA256Managed();
        hashedData = hashAlgo.ComputeHash(dataToSign);

        // sign Data using private key
        signature = ecDsaKeypair.SignHash(hashedData);
        string signatureBase64 = Convert.ToBase64String(signature);
        Console.WriteLine("signature (Base64): " + signatureBase64);

        // get public key from private key
        string ecDsaPublicKeyParametersXml = ecDsaKeypair.ToXmlString(ECKeyXmlFormat.Rfc4050);

        // verify
        Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");
        ECDsaCng ecDsaVerify = new ECDsaCng();
        bool signatureVerified = false;
        ecDsaVerify.FromXmlString(ecDsaPublicKeyParametersXml, ECKeyXmlFormat.Rfc4050);
        signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
        Console.WriteLine("signature verified: " + signatureVerified);
        }
        catch(ArgumentNullException) {
            Console.WriteLine("The data was not signed or verified");
        }
    }
}

微软已经决定,加密和哈希必须完全委托给.NET Framework中的操作系统。它是一半和一半,所以现在.NET 5和.NET Core有多个加密后端。例如,对于ECDsa,它有使用Windows服务的ECDsaCng和使用OpenSsl的Linux/MacOs的ECDsaOpenSsl。请参阅

现在。。。您的问题的解决方案是使用ECDsa类并让它选择其后端。它有一些问题。您不能轻松地将密钥导出为xml格式,也不能轻松地将它们导出为PEM格式。您可以轻松地将它们导出为字节[],也可以轻松地从PEM格式导入它们。这其实不是什么大问题,因为您很少需要生成密钥,通常您的程序从外部源接收密钥,或者如果它自己生成密钥,它可以将它们保存为二进制格式,以便以后重用

var dataToSignString = "Hello world!";
var dataToSign = Encoding.UTF8.GetBytes(dataToSignString);

Console.WriteLine("dataToSign: " + dataToSignString);

try
{
    Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");

    var ecDsaKeypair = ECDsa.Create(ECCurve.NamedCurves.nistP256);

    // Normally here:
    //ecDsaKeypair.ImportFromPem()

    Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);

    byte[] hashedData = null;
    byte[] signature = null;
    // create new instance of SHA256 hash algorithm to compute hash
    HashAlgorithm hashAlgo = new SHA256Managed();
    hashedData = hashAlgo.ComputeHash(dataToSign);

    // sign Data using private key
    signature = ecDsaKeypair.SignHash(hashedData);
    string signatureBase64 = Convert.ToBase64String(signature);
    Console.WriteLine("signature (Base64): " + signatureBase64);

    // get public key from private key
    string ecDsaPublicKeyParameters = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());

    // verify
    Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");

    var ecDsaVerify = ECDsa.Create(ECCurve.NamedCurves.nistP256);
    bool signatureVerified = false;

    // Normally here:
    //ecDsaKeypair.ImportFromPem()
    var publicKey = Convert.FromBase64String(ecDsaPublicKeyParameters);
    ecDsaVerify.ImportSubjectPublicKeyInfo(publicKey, out _);

    signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
    Console.WriteLine("signature verified: " + signatureVerified);
}
catch (ArgumentNullException)
{
    Console.WriteLine("The data was not signed or verified");
}
关于From/toxml格式,请参阅:

Mmmh根据已完成的一些测试,以PEM格式导出似乎相当容易:

public static IEnumerable<string> Split(string str, int chunkSize)
{
    for (int i = 0; i < str.Length; i += chunkSize)
    {
        yield return str.Substring(i, Math.Min(chunkSize, str.Length - i));
    }
}


请注意,我必须手动将字符串拆分为64个字符的块,这是rfc7468中给出的确切数字,因为Convert.ToBase64String仅支持76行长度。Microsoft已决定加密和哈希必须完全委托给.NET Framework中的操作系统,因为它是一半和一半,因此,现在.NET 5和.NET Core有多个用于加密的后端。例如,对于ECDsa,它有使用Windows服务的ECDsaCng和使用OpenSsl的Linux/MacOs的ECDsaOpenSsl。请参阅

现在。。。您的问题的解决方案是使用ECDsa类并让它选择其后端。它有一些问题。您不能轻松地将密钥导出为xml格式,也不能轻松地将它们导出为PEM格式。您可以轻松地将它们导出为字节[],也可以轻松地从PEM格式导入它们。这其实不是什么大问题,因为您很少需要生成密钥,通常您的程序从外部源接收密钥,或者如果它自己生成密钥,它可以将它们保存为二进制格式,以便以后重用

var dataToSignString = "Hello world!";
var dataToSign = Encoding.UTF8.GetBytes(dataToSignString);

Console.WriteLine("dataToSign: " + dataToSignString);

try
{
    Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");

    var ecDsaKeypair = ECDsa.Create(ECCurve.NamedCurves.nistP256);

    // Normally here:
    //ecDsaKeypair.ImportFromPem()

    Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);

    byte[] hashedData = null;
    byte[] signature = null;
    // create new instance of SHA256 hash algorithm to compute hash
    HashAlgorithm hashAlgo = new SHA256Managed();
    hashedData = hashAlgo.ComputeHash(dataToSign);

    // sign Data using private key
    signature = ecDsaKeypair.SignHash(hashedData);
    string signatureBase64 = Convert.ToBase64String(signature);
    Console.WriteLine("signature (Base64): " + signatureBase64);

    // get public key from private key
    string ecDsaPublicKeyParameters = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());

    // verify
    Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");

    var ecDsaVerify = ECDsa.Create(ECCurve.NamedCurves.nistP256);
    bool signatureVerified = false;

    // Normally here:
    //ecDsaKeypair.ImportFromPem()
    var publicKey = Convert.FromBase64String(ecDsaPublicKeyParameters);
    ecDsaVerify.ImportSubjectPublicKeyInfo(publicKey, out _);

    signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
    Console.WriteLine("signature verified: " + signatureVerified);
}
catch (ArgumentNullException)
{
    Console.WriteLine("The data was not signed or verified");
}
关于From/toxml格式,请参阅:

Mmmh根据已完成的一些测试,以PEM格式导出似乎相当容易:

public static IEnumerable<string> Split(string str, int chunkSize)
{
    for (int i = 0; i < str.Length; i += chunkSize)
    {
        yield return str.Substring(i, Math.Min(chunkSize, str.Length - i));
    }
}


请注意,我必须手动将字符串拆分为64个字符的块,这是rfc7468中给出的确切数字,因为Convert.ToBase64String只支持76行长度

Microsoft决定将.NET 5.0加密卸载到操作系统中。这意味着并非所有东西都在任何地方得到支持。有一个表显示了在哪里支持什么:如果你想成为多平台,你必须使用ECDsa。创建。。。然后处理您收到的对象。不确定您所需的一切是否都在通用ECDsaThe中实现ToXmlString不起作用,也不清楚如何将生成的密钥导出为PEM格式。有一些Import*Pem可以导入密钥。您可能需要根据上面的xantaos响应更新操作系统和内核。许多操作系统的旧版本不支持最新的加密模式。@jdweng:谢谢您的回复。由于我需要在联机编译器环境中运行代码,我无法更新联机编译器,抱歉。Microsoft决定将.NET 5.0加密卸载到操作系统。这意味着并非所有东西都在任何地方得到支持。有一个表显示了在哪里支持什么:如果你想成为多平台,你必须使用ECDsa。创建。。。然后处理您收到的对象。不确定您所需的一切是否都在通用ECDsaThe中实现ToXmlString不起作用,也不清楚如何将生成的密钥导出为PEM格式。有一些Import*Pem可以导入密钥。您可能需要根据上面的xantaos响应更新操作系统和内核。许多操作系统的旧版本不支持最新的加密模式。@jdweng:谢谢您的回复。由于我需要在联机编译器环境中运行我的代码,我无法更新联机编译器,对不起。非常感谢。当我在一个跨平台项目上工作时,签署了一份
在C、Java、WebCrypto、PHP和NodeJs中进行nd验证,我将在PEM编码中生成一次密钥对,并在整个平台上使用它。您的代码正在运行,因此我可以提供运行的解决方案。请注意:该代码不再在Windows上运行错误消息:“ECDsa”不包含“ImportPkcs8PrivateKey”的定义,并且找不到接受类型为“ECDsa”的第一个参数的扩展方法“ImportPkcs8PrivateKey”是否缺少using指令或汇编参考?但这对我来说并不重要-更重要的是在线版本正在运行。@MichaelFehr该方法出现在.NET5中。。。可能是他们在那个版本中介绍的。非常感谢。当我在使用C、Java、WebCrypto、PHP和NodeJs进行跨平台项目签名和验证时,我将在PEM编码中生成一次密钥对,并在整个平台上使用它。您的代码正在运行,因此我可以提供运行的解决方案。请注意:该代码不再在Windows上运行错误消息:“ECDsa”不包含“ImportPkcs8PrivateKey”的定义,并且找不到接受类型为“ECDsa”的第一个参数的扩展方法“ImportPkcs8PrivateKey”是否缺少using指令或汇编参考?但这对我来说并不重要-更重要的是在线版本正在运行。@MichaelFehr该方法出现在.NET5中。。。可能是他们在那个版本中介绍的。
string b64privateKey = Convert.ToBase64String(ecDsaKeypair.ExportPkcs8PrivateKey());
b64privateKey = string.Join("\r\n", Split(b64privateKey, 64));
string pemPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" + b64privateKey + "\r\n-----END PRIVATE KEY-----";
string b64publicKey = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());
b64publicKey = string.Join("\r\n", Split(b64publicKey, 64));
string pemPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" + b64publicKey + "\r\n-----END PUBLIC KEY-----";