I've been going in circles trying to get a simple ed25519 signature verification working. Had success with libsodium third party library but then ran into issues with it and am back to the drawing board. I see system.security.cryptography.ecdsa but it's not clear to me if it's possible to get that to work with a ed25519 signature. I'm kind of surprised there aren't code examples of doing this sig verification in .Net because I thought ed25519 was a reasonably common algorithm?
Is there a Microsoft library for ed25519 signature verification? Or can anyone provide an example of how to successfully do this in .Net?
You can use BouncyCastle for this purpose. Below is the code example of how I am doing it. For using this code make sure the publicKey and signature are hex strings.
public Task<bool> VerifySignature(string publicKey, string dataToVerify, string signature)
{
var publicKeyParam = new Ed25519PublicKeyParameters(Hex.DecodeStrict(publicKey));
var dataToVerifyBytes = Encoding.UTF8.GetBytes(dataToVerify);
var signatureBytes = Convert.FromHexString(signature);
var verifier = new Ed25519Signer();
verifier.Init(false, publicKeyParam);
verifier.BlockUpdate(dataToVerifyBytes, 0, dataToVerifyBytes.Length);
var isVerified = verifier.VerifySignature(signatureBytes);
return Task.FromResult(isVerified);
}
Related
I'm currently using this link to store my RSA key in windows Key Container (machine-level) and it works fine, but I'm looking for a way that works for both Linux and windows because I will definitely deploy this project on Linux.
public static void StoreRSAKey(string containerName, string xmlKey)
{
#pragma warning disable CA1416 // Validate platform compatibility
var parameters = new CspParameters
{
KeyContainerName = containerName
};
#pragma warning restore CA1416 // Validate platform compatibility
parameters.Flags = CspProviderFlags.UseMachineKeyStore;
using RSACryptoServiceProvider? rsa = new RSACryptoServiceProvider(parameters);
rsa.FromXmlString(xmlKey);
}
I have found several recommendations on the web but I need a more precise solution.
I'd be glad if anyone can help me through this.
Best way to store RSA key and use it in .Net core(looking for a cross-platform solution)
IMO, the question should be, is it possible to use the RSA key in .net core for cross-platform?
I have recently built open-source encryption and decryption library, spending hours investigating the same question you asked. The short answer it is not possible to use CspParameters with Linux, it is for Windows OS (as this answer mentions). And because the answer is not possible, there is no best way.
So to start with, let's see if we can answer the question of using the RSA key in the .net core for cross-platform.
To do that it is very simple, you need to do following:
Rsa = RSA.Create();
Rsa.KeySize = 2048;
This part does not require installing the library, it is part of netstandard2.0.
That is it, now to export and import a key that you generate, you can do the following.
When you RSA.Create() first you can export the key and store it anywhere safe for later usage.
To export private key and it should be kept safe
Rsa.ToXmlString(true);
To export public key, to encrypt with
Rsa.ToXmlString(false);
When you need to import the key from a local store, you can do the following:
Rsa.FromXmlString(asymmetricKey);
This is the cross-platform compatible solution for windows, Linux, or Mac computers.
It is also possible to import certificates from a local computer using X509Certificate2 and use its public key for encryption and private key for decryption.
It is also possible to import private key parameters to RSAParameters, which requires a helper method to translate XML tags from private key file:
<RSAKeyValue>
<Modulus>xxxx...</Modulus>
<Exponent>xxxx</Exponent>
<P>xxxx...</P>
<Q>xxxx...</Q>
<DP>xxxx...</DP>
<DQ>xxxx...</DQ>
<InverseQ>xxxx...</InverseQ>
<D>xxxx...</D>
</RSAKeyValue>
But I find it easier to use FromXmlString and it is part of RSA class when creating RSA.Create(), so no need for a helper method, that said if performance means a lot for your project, you need to make a performance test to compare the results.
So finally I provide a simple example of how to store and load keys:
public static void Main(string[] args)
{
var rsa = RSA.Create();
rsa.KeySize = 2048;
// public key for decrypting
var privateKey = rsa.ToXmlString(true);
SaveKey(#"privateKey", privateKey);
// public key for encrypting
var publicKey = rsa.ToXmlString(false);
SaveKey(#"publicKey", publicKey);
// initialize the private for use on another instance
var rsaAnotherPlace = RSA.Create();
rsaAnotherPlace.KeySize = 2048;
rsaAnotherPlace.FromXmlString(LoadKey(#"privateKey"));
}
// store my keys
public static void SaveKey(string filename, string content)
{
var bytes = Encoding.ASCII.GetBytes(content);
using var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
}
// load key
public static string LoadKey(string filename)
{
var bytes = File.ReadAllBytes(filename);
return Encoding.ASCII.GetString(bytes);
}
I have tested the solution on Windows and Linux OS and it passes the macOS test on GitHub actions, but I have not tested it yet on macOS.
Disclaimer: this is the open-source library I am working on.
I am writing a Java Card 3.0.2 application on a NXP J3D081 card. I have it both signing and verifying a signature using ALG_ECDSA_SHA_256. The keys have been written to the card by my test app. If I sign 32 bytes of data and pass the signature back to the card the Verify code successfully verifies the signature. If I sign 32 bytes in Bouncy Castle with the Private key and pass to the Verify on the Card it successfully verifies the signature. The bouncy castle Verify Code successfully verifies signatures created from the bouncy castle signing routine.
BUT if I take the returned signature from the Java Card and pass it to the C# bouncy castle code it FAILS to verify the signature. I have checked all input values and they are correct. My code is here (note I pass Public keys as 64 bytes and prepend them with 0x04)
public bool HashAndVerifyDSA(byte[] pb, byte[] inData, byte[] sig)
{
byte[] pub = new byte[65];
pub[0] = 0x4;
Array.Copy(pb, 0, pub, 1, 64);
ECCurve curve = parameters.Curve;
ECPoint q = curve.DecodePoint(pub);
ICipherParameters Public = new ECPublicKeyParameters(algorithm, q, parameters);
ISigner bSigner = SignerUtilities.GetSigner("SHA-256withECDSA");
bSigner.Init(false, Public);
bSigner.BlockUpdate(inData, 0, inData.Length);
return (bSigner.VerifySignature(sig));
}
I should note that the parameters specify the P-256 curve and are used successfully in the encrypted communication to the card. The Public key is successfully created.
I seem to have less hair now then I did two days ago. Any pointers would be welcome.
Apart from steps you have performed to debug the thing, you can check the following also: -
Verify the signature using some online available tool. Do not forget to use same curve parameters and public key generated from javacard.
Verify the same using bouncy castle java library. I perform the same steps in one of my tools and it was matched successfully.
I want to create web of trust support in my application, allowing my users to use their private keys, to sign other user's public keys - Using C# and Bouncy Castle.
I've got most things figured out, such as creating PGP keys, submitting them to key servers using HTTP REST, encrypting MIME messages and cryptographically signing them (using MimeKit) - But the one remaining hurdle, is to figure out some piece of code that can use my private key, to sign for another person's public key, using Bouncy Castle.
Since the documentation for BC is horrendous, figuring out these parts, have previously proven close to impossible ...
For the record, I'm using GnuPG as my storage for keys.
If anybody wants to look at my code so far for what I have done, feel free to check it out here.
I am probably not supposed to ask this here, but I'd also love it if some BC gurus out there could have a general look at my code so far, and check if I've made a fool of myself with the stuff I've done so far ...
Found the answer after a lot of trial and error, here it is ...
private static byte[] SignPublicKey(
PgpSecretKey secretKey,
string password,
PgpPublicKey keyToBeSigned,
bool isCertain)
{
// Extracting private key, and getting ready to create a signature.
PgpPrivateKey pgpPrivKey = secretKey.ExtractPrivateKey (password.ToCharArray());
PgpSignatureGenerator sGen = new PgpSignatureGenerator (secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
sGen.InitSign (isCertain ? PgpSignature.PositiveCertification : PgpSignature.CasualCertification, pgpPrivKey);
// Creating a stream to wrap the results of operation.
Stream os = new MemoryStream();
BcpgOutputStream bOut = new BcpgOutputStream (os);
sGen.GenerateOnePassVersion (false).Encode (bOut);
// Creating a generator.
PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
PgpSignatureSubpacketVector packetVector = spGen.Generate();
sGen.SetHashedSubpackets (packetVector);
bOut.Flush();
// Returning the signed public key.
return PgpPublicKey.AddCertification (keyToBeSigned, sGen.Generate()).GetEncoded();
}
Earlier our Application-A was in C++ and a message was signed before sending it to Application-B using crypto API functions in C++ , exactly similar to the example described in http://msdn.microsoft.com/en-us/library/windows/desktop/aa382372%28v=vs.85%29.aspx.
This message was again verified by Application-B using Crypto API functions in C++ (the above example again talks about how to verify an already signed message).
Now we are in the process of converting/migrating the old C++ Application-A to C#.
I already found a way to sign the message using P-Invoke in C# and when the signed message was verified by Application-B (using C++ CryptVerifySignatureMessage) everything is working fine.
Example is available in - http://blogs.msdn.com/b/alejacma/archive/2008/02/21/how-to-sign-a-message-and-verify-a-message-signature-c.aspx .
As #CodeInChaos has mentioned in his comments i want the leave the interop work to the framework (without using P-Invoke or other 3rd party implementation like BountyCastle)
So would like to know whether .net offers any API to sign a message (as a learning perspective too) , if so how can i achieve it.
NOTE:
I already tried crypto wrapper API RSACryptoServiceProvider offered by .Net.
private byte[] SignData(byte[] data, string certThumbPrint)
{
X509Certificate2 cert = GetCertificate(); // finds the certificate with thumbprint
RSACryptoServiceProvider rsaCryptoServiceProvider = (RSACryptoServiceProvider)cert.PrivateKey;
return rsaCryptoServiceProvider.SignData(data, new SHA1CryptoServiceProvider());
}
But found a major difference with the return value (byte array) of CryptSignMessage from C++ and RSACryptoServiceProvider.SignData() method from C#.
• CryptSignMessage: The CryptSignMessage function creates a hash of the specified content, signs the hash, and then encodes both the original message content and the signed hash.
• RSA.SignData: Computes the hash value of the specified byte array using the specified hash algorithm, and signs the resulting hash value.
Because of this difference , the Application-B when it verifies the message it throws error saying 'invalid signing' .
So i cant use this RSACryptoServiceProvider type offered by .net.
Is there any other way to achieve the same using any .NET API's ? (when using .net API the output byte array should be similar to that of output when using PInvoke example as mentioned above) so that Application-B can work without any issues.
Any help is appreciated.
After some long research i found a way to do it . If someone else is looking for how to sign a message using PKCS7 format using a certificate in C# then here it is,
public byte[] SignMsg(
Byte[] msg,
X509Certificate2 signerCert)
{
// Place message in a ContentInfo object.
// This is required to build a SignedCms object.
ContentInfo contentInfo = new ContentInfo(msg);
// Instantiate SignedCms object with the ContentInfo above.
// Has default SubjectIdentifierType IssuerAndSerialNumber.
// Has default Detached property value false, so message is
// included in the encoded SignedCms.
SignedCms signedCms = new SignedCms(contentInfo);
// Formulate a CmsSigner object, which has all the needed
// characteristics of the signer.
CmsSigner cmsSigner = new CmsSigner(signerCert);
// Sign the PKCS #7 message.
Console.Write("Computing signature with signer subject " +
"name {0} ... ", signerCert.SubjectName.Name);
signedCms.ComputeSignature(cmsSigner);
Console.WriteLine("Done.");
// Encode the PKCS #7 message.
return signedCms.Encode();
}
Found the information from the link http://msdn.microsoft.com/en-us/library/ms180961%28v=vs.85%29.aspx .
My component is responsible for downloading files from the server. As part of file validation I have used CAPICOM (SignedCode object) to validate if a certificate contains a specific string and call to Validate method of the SignedCode object. In case the file contains certificate without a requested string in the name, user was prompted if he trust this file.
Since CAPICOM going to be deprecated by Microsoft, I need to implement these logic using .NET libraries.
How I can get the same functionality using .NET libraries?
Is there any example on the web?
Thanks
Zaky
using System.Security.Cryptography;
// ....
byte[] SignData(byte[] toSign)
{
RSACryptoServiceProvider rsaCert =
GetCertificateWithPrivateKeyFromSomewhere(); // this method is yours
return rsaCert.SignData(toSign, new SHA1CryptoServiceProvider());
}
bool VerifyData(byte[] toVerify, byte[] signature)
{
RSACryptoServiceProvider rsaCert =
GetCertificateWithPublicKeyFromSomewhere(); // this method is yours
return rsaCert.VerifyData(toVerify, new SHA1CryptoServiceProvider(), signature);
}