在线编译器中的 C# ECDSA 签名 运行 失败
C# ECDSA signature running in an online compiler fails
我 运行 在我的 Windows 机器上成功地 运行ning 这段代码(Win 10 x64,运行ning dotnet 4.7.2)。它生成一个 EC 密钥对(“P-256”),使用 SHA-256 对明文进行哈希处理,使用 ec 私钥对散列进行签名,并使用 ec public 密钥针对散列明文验证签名。
我得到了这个输出,所以一切正常:
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
现在我正试图找到任何能够 运行 代码的在线编译器。我最喜欢的编译器
(https://repl.it/, Mono C# compiler version 6.8.0.123, full code: https://repl.it/@javacrypto/EcSignatureFull#main.cs) 运行正在进入此错误:
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/, Compiler .net 5, full code: https://dotnetfiddle.net/lSPpjz) 会出现类似的错误:
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 站点更适合我的问题?
警告:以下代码没有异常处理,仅供学习使用:
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 已决定加密和散列必须完全委托给 OS(在 .NET Framework 中是一半一半),所以现在 .NET 5(和 .NET Core)有多个后端用于加密(例如对于 ECDsa
它有 ECDsaCng
使用 Windows 服务和 ECDsaOpenSsl
对于 Linux/MacOs 使用 OpenSsl(见 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
,current comment on them on the github of .NET Core is:
// 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 行长度)
我 运行 在我的 Windows 机器上成功地 运行ning 这段代码(Win 10 x64,运行ning dotnet 4.7.2)。它生成一个 EC 密钥对(“P-256”),使用 SHA-256 对明文进行哈希处理,使用 ec 私钥对散列进行签名,并使用 ec public 密钥针对散列明文验证签名。
我得到了这个输出,所以一切正常:
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
现在我正试图找到任何能够 运行 代码的在线编译器。我最喜欢的编译器 (https://repl.it/, Mono C# compiler version 6.8.0.123, full code: https://repl.it/@javacrypto/EcSignatureFull#main.cs) 运行正在进入此错误:
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/, Compiler .net 5, full code: https://dotnetfiddle.net/lSPpjz) 会出现类似的错误:
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 站点更适合我的问题?
警告:以下代码没有异常处理,仅供学习使用:
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 已决定加密和散列必须完全委托给 OS(在 .NET Framework 中是一半一半),所以现在 .NET 5(和 .NET Core)有多个后端用于加密(例如对于 ECDsa
它有 ECDsaCng
使用 Windows 服务和 ECDsaOpenSsl
对于 Linux/MacOs 使用 OpenSsl(见 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
,current comment on them on the github of .NET Core is:
// 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 行长度)