簡體   English   中英

在在線編譯器中運行的 C# ECDSA 簽名失敗

[英]C# ECDSA signature running in an online compiler fails

我在我的 Windows 機器(Win 10 x64,運行 dotnet 4.7.2)上成功運行了此代碼。 它生成一個 EC 密鑰對(“P-256”),使用 SHA-256 對明文進行哈希處理,使用 ec 私鑰對 hash 進行簽名,並使用 ec 公鑰根據哈希明文驗證簽名。

我得到這個 output 所以一切正常:

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

現在我正在嘗試找到任何能夠運行代碼的在線編譯器。 My favorite compiler ( https://repl.it/ , Mono C# compiler version 6.8.0.123, full code: https://repl.it/@javacrypto/EcSignatureFull#main.cs ) is running into this error:

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

使用另一個平台( https://dotnetfiddle.net/ ,編譯器 .net 5,完整代碼: https://dotnetfiddlenet給出了類似的錯誤:

Unhandled exception. System.PlatformNotSupportedException: Windows Cryptography Next Generation (CNG) is not supported on this platform.
   at System.Security.Cryptography.ECDsaCng..ctor(Int32 keySize)
   at EcSignatureString.Main()
Command terminated by signal 6

所以我的問題是:是否有任何可用的在線編譯器能夠運行代碼?

我認為我的問題可能與 SO 無關——在這種情況下——是否還有其他 stackexchange-site 可以更好地解決我的問題?

警告:以下代碼沒有異常處理,僅用於教育目的:

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");
        }
    }
}

Microsoft has decided that encryption and hashing must be fully delegated to the OS (in .NET Framework it was half and half), so now .NET 5 (and .NET Core) has multiple backends for encryption (for example for ECDsa it has ECDsaCng that uses Windows使用 OpenSsl 的 Linux/MacO 服務和ECDsaOpenSsl (請參閱MSDN

現在......您的問題的解決方案是使用ECDsa class 並讓它 select 作為其后端。 它有一些問題。 您不能輕松地將密鑰導出為 xml 格式,也不能輕松地將它們導出為 PEM 格式。 您可以輕松地將它們導出為byte[] ,並且可以輕松地從 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/ToXmlFormat當前對 .NET Core 的 github 的評論是

// There is currently not a standard XML format for ECC keys, so we will not implement the default
// To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
// use an overload which allows the user to specify the format they'd like to serialize into.

嗯,從完成的一些測試來看,以 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));
    }
}

接着

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-----";

(請注意,我必須手動將字符串拆分為 64 個字符的塊,這是 rfc7468 中給出的確切數字,因為Convert.ToBase64String()僅支持 76 行長度)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM