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);
}
Related
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);
}
We are generating some self-signed certificates for testing using BouncyCastle, but the code throws an exception when we try to add a private key to the certificate. Here's the code in question:
private static X509Certificate2 CreateCertificate(string subject, DateTimeOffset notBefore, DataTimeOffset notAfter, string issuer, AsymmetricKeyParamter issuerPrivateKey)
{
// Setup
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
SecureRandom random = new SecureRandom(new CryptoApiRandomGenerator());
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(new KeyGenerationParameters(random, KeyStrength));
// Randomly generate a serial number
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
certGenerator.SetSerialNumber(serialNumber);
// Set the issuer and subject names
X509Name issuerName = new X509Name(issuer);
X509Name subjectName = new X509Name(subject);
certGenerator.SetIssuerDN(issuerName);
certGenerator.SetSubjectDN(subjectName);
// Set the validity period
certGenerator.SetNotBefore(notBefore.UtcDateTime);
certGenerator.SetNotAfter(notAfter.UtcDateTime);
// Randomly generate the public key
AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certGenerator.SetPublicKey(subjectKeyPair.Public);
// Generate the signed certificate
ISignatureFactory signatureFactory = new Asn1SignatureFactory(SHA256RSASignatureAlgorithm, issuerPrivateKey ?? subjectKeyPair.Private, random);
X509Certificate2 certificate = new X509Certificate2(certGenerator.Generate(signatureFactory).GetEncoded());
// Include the private key with the response
// ERROR HERE!
certificate.PrivateKey = DotNetUtilities.ToRSA(subjectKeyPair.Private as RsaPrivateCrtKeyParameters);
return certificate;
}
This code is in a library that targets .NET Standard 2.0, and the library is a dependency of two different applications: one targeting .NET Core 2.1 and the other targeting .NET Framework 4.7.2. I believe this works fine in the .NET Framework app, but in the .NET Core app I'm getting an exception with this message on the indicated line above:
Operation is not supported on this platform.
Apparently this is expected behavior in .NET Core. I am aware of the CopyWithPrivateKey method as mentioned in this question, which in theory is what I should be using. However, this method is not supported in .NET Standard 2.0 (note the error at the top of the page indicating the redirect). Furthermore, the .NET Framework app cannot be converted to .NET Core at the moment because of some other dependencies which are .NET Framework. According to this matrix, .NET Standard 2.1 is not supported by .NET Framework at all, which means I cannot upgrade to .NET Standard 2.1 and use CopyWithPrivateKey!
How can I create an X509Certificate2 with a private key in .NET Standard 2.0 in a way that's compatible with .NET Core?
After much digging, the only solution I found was to convert the certificate to a PKCS12-formatted byte array, append the private key, and then read it back into an X509Certificate2 object. It sucks, but from what I can tell in .NET Core the only way you can get one with the private key is to either call CopyWithPrivateKey (unavailable in .NET Standard 2.0 as mentioned in the question) or to load PKCS12 data which contains the private key. The following code can do that:
private static X509Certificate2 AddPrivateKeyPlatformIndependent(Org.BouncyCastle.X509.X509Certificate bouncyCastleCert, AsymmetricKeyParameter privateKey)
{
string alias = bouncyCastleCert.SubjectDN.ToString();
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
X509CertificateEntry certEntry = new X509CertificateEntry(bouncyCastleCert);
store.SetCertificateEntry(alias, certEntry);
// TODO: This needs extra logic to support a certificate chain
AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privateKey);
store.SetKeyEntry(alias, keyEntry, new X509CertificateEntry[] { certEntry });
byte[] certificateData;
string password = GenerateRandomString();
using (MemoryStream memoryStream = new MemoryStream())
{
store.Save(memoryStream, password.ToCharArray(), new SecureRandom());
memoryStream.Flush();
certificateData = memoryStream.ToArray();
}
return new X509Certificate2(certificateData, password, X509KeyStorageFlags.Exportable);
}
Use this in place of the last two lines in the question code, and instead of creating an X509Certificate2 with certGenerator, use this to create the equivalent Bouncy Castle Type to pass to it: Org.BouncyCastle.X509.X509Certificate bouncyCastleCert = certGenerator.Generate(signatureFactory);
Since documentation on Bouncy Castle and .NET certificates is sparse at best, here are some quirks I found during this process:
The aliases for the certificate entry and the key entry in the PKCS12 container must be the same. Otherwise, you will get a "Keyset does not exist" exception when trying to use the private key.
You must use X509KeyStorageFlags.Exportable when loading the byte array, otherwise you may get a "Key not valid for use in specified state" exception depending on how you use the cert. This also means you have to supply a password because there's no overload without it, but you can use any old string since it's a temporary password.
I have a .crt certificate and a .key private key file on a Linux machine. The private key is in encrypted PKCS#8 format (BEGIN ENCRYPTED PRIVATE KEY...). I would like to import these into an X509Certificate2 object for further use. Since we're on Linux, we're using .NET Core 2.2 (we cannot migrate to 3.0 yet).
I have explored a few possible solutions, detailed below:
Use openssl to convert the files to a .pfx and import that using X509Certificate2
I do not want to use this option since I don't want to execute shell code from within C#. I would like the solution to be completely programmatically achieved in C#.
Use the C# BouncyCastle libraries to do either:
A conversion of both the certificate and the key to .pfx (as above), or
Importing the certificate and private key separately and using X509Certificate2.CopyWithPrivateKey() to combine them.
However, I cannot find an API for the C# version of BouncyCastle, so I'm not sure what methods I could possibly use to do this.
Some other programmatic method in C# that I'm missing here
Essentially, the end goal is to obtain an X509Certificate2 object from the .crt and .key files. Any help/insight into what approach to use, or even a pointer to helpful BouncyCastle documentation, would be much appreciated. Thanks!
This is possible, though not as friendly as it could be, in .NET Core 3.0:
private static byte[] UnPem(string pem)
{
// This is a shortcut that assumes valid PEM
// -----BEGIN words-----\nbase64\n-----END words-----
const string Dashes = "-----";
int index0 = pem.IndexOf(Dashes);
int index1 = pem.IndexOf('\n', index0 + Dashes.Length);
int index2 = pem.IndexOf(Dashes, index1 + 1);
return Convert.FromBase64String(pem.Substring(index1, index2 - index1));
}
...
string keyPem = File.ReadAllText("private.key");
byte[] keyDer = UnPem(keyPem);
X509Certificate2 certWithKey;
using (X509Certificate2 certOnly = new X509Certificate2("certificate.cer"))
using (RSA rsa = RSA.Create())
{
// For "BEGIN PRIVATE KEY"
rsa.ImportPkcs8PrivateKey(keyDer, out _);
certWithKey = certOnly.CopyWithPrivateKey(rsa);
}
using (certWithKey)
{
Console.WriteLine(certWithKey.HasPrivateKey);
}
RSA private keys can be in three different formats, and you need to call the correct import for each one:
"BEGIN PRIVATE KEY": ImportPkcs8PrivateKey
"BEGIN ENCRYPTED PRIVATE KEY": ImportEncryptedPkcs8PrivateKey
"BEGIN RSA PRIVATE KEY": ImportRSAPrivateKey
In .NET 5 this got as easy as can be:
var certificate =
X509Certificate2.CreateFromPemFile(
crtFile,
Path.ChangeExtension(crtFile, "key"));
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 .