I'd like to write a program which monitors CRL (Certificate Revocation List) expiration date.
Therefore, I'd like to read the following properties from a CRL file:
1) Effective Date
2) Next Update
3) Next CRL Publish
How can I accomplish my task?
I've only managed to find types for X509Certificate2, X509Chain, x509RevocationMode etc..
you can use the class X509Certificate2 to get information needed.
example:To handle one certification file
X509Certificate2 x509 = new X509Certificate2();
byte[] rawData = ReadFile(fname);
x509.Import(rawData);
var validDate= x509 . NotBefore;
var expireDate = x509.NotAfter;
//Reads a file.
internal static byte[] ReadFile (string fileName)
{
FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read);
int size = (int)f.Length;
byte[] data = new byte[size];
size = f.Read(data, 0, size);
f.Close();
return data;
}
reference:
https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2(v=vs.110).aspx
Edit:
You can use the BouncyCastle.Crypto library to handle CRL.
Download the library and reference the BouncyCastle.Crypto.dll
or instal the nuget package:
Install-Package BouncyCastle
//reference library BouncyCastle.Crypto
//http://www.bouncycastle.org/csharp/
//Load CRL file and access its properties
public void GetCrlInfo(string fileName, Org.BouncyCastle.Math.BigInteger serialNumber, Org.BouncyCastle.X509.X509Certificate cert)
{
try
{
byte[] buf = ReadFile(fileName);
X509CrlParser xx = new X509CrlParser();
X509Crl ss = xx.ReadCrl(buf);
var nextupdate = ss.NextUpdate;
var isRevoked = ss.IsRevoked(cert);
Console.WriteLine("{0} {1}",nextupdate,isRevoked);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Although, the question is answered, I would add that there is another good open project that extends native .NET Framework to work with cryptography objects which are missing in the .NET: https://github.com/Crypt32/pkix.net
In regards to CRL, I developed an X509CRL2 class in a similar way to built-in X509Certificate2: X509CRL2 Class. The usage is pretty simple:
// reference System.Security.Cryptography.X509Certificates namespace
var crl = new X509CRL2(#"C:\temp\crlfile.crl");
// Effective date:
var effective = crl.ThisUpdate;
// next update:
var nextupdate = crl.NextUpdate;
// next publish:
var nextPublishExtension = crl.Extensions["1.3.6.1.4.1.311.21.4"];
if (nextPublishExtension != null) { nextPublishExtension.Format(1); }
I support CRL files in multiple formats, including pure binary, Base64 or even in hex.
By using this class you can not only read CRL properties, but you can generate Version 2 CRLs.
Note: pkix.net library relies on my another open project https://github.com/Crypt32/Asn1DerParser.NET which is used to parse ASN structures.
in addition to M.Hassan post ;
Using BouncyCastle.X509 you must convert the System.Security... X509Certificate2 to BouncyCastle certificate, the missing function between initial code and edit could be :
using System.Security.Cryptography.X509Certificates;
public static Org.BouncyCastle.X509.X509Certificate Convert(X509Certificate2 certificate)
{
var certificateParser = new Org.BouncyCastle.X509.X509CertificateParser();
var rawData = certificate.GetRawCertData();
var bouncyCertificate = certificateParser.ReadCertificate(rawData);
return bouncyCertificate;
}
We can use CertEnroll win32 APIs. The code can be
var bytes = File.ReadAllBytes(crlFile);
var base64 = System.Convert.ToBase64String(bytes);
CX509CertificateRevocationList crl = new CX509CertificateRevocationList();
crl.InitializeDecode(base64, EncodingType.XCN_CRYPT_STRING_BASE64_ANY);
Add the following to the csproj for including certEnrol
<ItemGroup>
<COMReference Include="CERTENROLLLib">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>728ab348-217d-11da-b2a4-000e7bbb2b09</Guid>
<Lcid>0</Lcid>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
Related
I'm working with a Trust Commerce Tutorial on how to generate a payment token that will allow customers to use the TC Trustee Host payment form. I was given an example on how to retrieve this token by their dev team.
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;
using System.Web;
/** #class TCToken
* An example class for generating a TrustCommerce Trustee Token
*/
public class TCToken
{
public static void Main(string [] args)
{
string custid = "123456";
string password = "XXXXXX";
try {
// Adapted from http://www.west-wind.com/presentations/dotnetWebRequest/dotnetWebRequest.htm
string gateway_post_address = "https://vault.trustcommerce.com/trustee/token.php";
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(gateway_post_address);
// A sixty second timeout.
req.Timeout = 60000;
string post_data = "custid=" + HttpUtility.UrlEncode(custid) +
"&password=" + HttpUtility.UrlEncode(password);
req.Method = "POST";
byte [] buf = System.Text.Encoding.GetEncoding(1252).GetBytes(post_data);
req.ContentLength = buf.Length;
req.ContentType = "application/x-www-form-urlencoded";
Stream s = req.GetRequestStream();
s.Write(buf, 0, buf.Length);
s.Close();
HttpWebResponse rep = (HttpWebResponse) req.GetResponse();
Encoding enc = System.Text.Encoding.GetEncoding(1252);
StreamReader rs = new StreamReader(rep.GetResponseStream(), enc);
string token = rs.ReadToEnd();
Console.WriteLine(token);
rep.Close();
rs.Close();
} catch (Exception e) {
Console.WriteLine(e);
}
}
}
I made a new console application in visual studio, copied this code, and replaced the username and password with the correct credentials. When I try to run this, I get the following error in the console.
System.NotSupportedException: No data is available for encoding 1252.
For information on defining a custom encoding, see the documentation
for the Encoding.RegisterProvider method. at
System.Text.Encoding.GetEncoding(Int32 codepage) at
TCToken.Program.Main(String[] args) in
C:\Users\xxxx\source\repos\TCToken\TCToken\Program.cs:line 29
I've tried to google this error and most of the responses are a little above my understanding. I'm certainly not a C# expert.
What ckuri said. Just to be clear, you need the following line of code before opening the stream (steps 2,3):
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
ExcelDataReader - Important note on .NET Core
By default, ExcelDataReader throws a NotSupportedException "No data is
available for encoding 1252." on .NET Core.
To fix, add a dependency to the package System.Text.Encoding.CodePages
and then add code to register the code page provider during
application initialization (f.ex in Startup.cs):
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
This is required to parse strings in binary BIFF2-5 Excel documents
encoded with DOS-era code pages. These encodings are registered by
default in the full .NET Framework, but not on .NET Core.
.NET Core supports only ASCII, ISO-8859-1 and Unicode encodings, whereas .NET Framework supports much more.
However, .NET Core can be extended to support additional encodings like Windows-1252, Shift-JIS, GB2312 by registering the CodePagesEncodingProvider from the System.Text.Encoding.CodePages NuGet package.
After the NuGet package is installed the following steps as described in the documentation for the CodePagesEncodingProvider class must be done to register the provider:
Add a reference to the System.Text.Encoding.CodePages.dll assembly to your project.
Retrieve a CodePagesEncodingProvider object from the static Instance property.
Pass the CodePagesEncodingProvider object to the Encoding.RegisterProvider method.
nuget:
Install-Package System.Text.Encoding.CodePages -Version 5.0.0
code:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
I was experiencing similar issue when I was trying to read and convert xlsx file to DataTable. I found out that encoding 1252 are not default in .NET Core therefore I had to separately add NuGet package for the same.
Below is the method where I convert the data from memory stream.
private static DataTableCollection ExcelToDataTable(MemoryStream stream)
{
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
var result = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (data) => new ExcelDataTableConfiguration()
{
UseHeaderRow = true
}
});
return result.Tables;
}
}
I referenced the Encoder from nuGet Package at the start of the method and it worked fine for me. This answer is late but might help people who are reading data from streams.
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Solution:
Add the System.Text.Encoding.CodePages Package to your project.
Write this code in your program:
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
I've installed the library System.Text.Encoding.CodePages like other posts said and this code worked for me:
System.Text.Encoding.RegisterProvider(
System.Text.CodePagesEncodingProvider.Instance);
Encoding srcEncoding = Encoding.GetEncoding(1251);
using (var reader = new StreamReader(#"D:\someFile.csv", encoding: srcEncoding))
{
List<string> listA = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
listA.Add(values[0]);
}
}
I'm working on a project that I need to create zip with password protected from file content in c#.
Before I've use System.IO.Compression.GZipStream for creating gzip content.
Does .net have any functionality for create zip or rar password protected file?
Take a look at DotNetZip (#AFract supplied a new link to GitHub in the comments)
It has got pretty geat documentation and it also allow you to load the dll at runtime as an embeded file.
Unfortunately there is no such functionality in the framework. There is a way to make ZIP files, but without password. If you want to create password protected ZIP files in C#, I'd recommend SevenZipSharp. It's basically a managed wrapper for 7-Zip.
SevenZipBase.SetLibraryPath(Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Environment.CurrentDirectory,
"7za.dll"));
SevenZipCompressor compressor = new SevenZipCompressor();
compressor.Compressing += Compressor_Compressing;
compressor.FileCompressionStarted += Compressor_FileCompressionStarted;
compressor.CompressionFinished += Compressor_CompressionFinished;
string password = #"whatever";
string destinationFile = #"C:\Temp\whatever.zip";
string[] sourceFiles = Directory.GetFiles(#"C:\Temp\YourFiles\");
if (String.IsNullOrWhiteSpace(password))
{
compressor.CompressFiles(destinationFile, sourceFiles);
}
else
{
//optional
compressor.EncryptHeaders = true;
compressor.CompressFilesEncrypted(destinationFile, password, sourceFiles);
}
DotNetZip worked great in a clean way.
DotNetZip is a FAST, FREE class library and toolset for manipulating zip files.
Code
static void Main(string[] args)
{
using (ZipFile zip = new ZipFile())
{
zip.Password = "mypassword";
zip.AddDirectory(#"C:\Test\Report_CCLF5\");
zip.Save(#"C:\Test\Report_CCLF5_PartB.zip");
}
}
I want to add some more alternatives.
For .NET one can use SharpZipLib, for Xamarin use SharpZipLib.Portable.
Example for .NET:
using ICSharpCode.SharpZipLib.Zip;
// Compresses the supplied memory stream, naming it as zipEntryName, into a zip,
// which is returned as a memory stream or a byte array.
//
public MemoryStream CreateToMemoryStream(MemoryStream memStreamIn, string zipEntryName) {
MemoryStream outputMemStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(outputMemStream);
zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
zipStream.Password = "Your password";
ZipEntry newEntry = new ZipEntry(zipEntryName);
newEntry.DateTime = DateTime.Now;
zipStream.PutNextEntry(newEntry);
StreamUtils.Copy(memStreamIn, zipStream, new byte[4096]);
zipStream.CloseEntry();
zipStream.IsStreamOwner = false; // False stops the Close also Closing the underlying stream.
zipStream.Close(); // Must finish the ZipOutputStream before using outputMemStream.
outputMemStream.Position = 0;
return outputMemStream;
// Alternative outputs:
// ToArray is the cleaner and easiest to use correctly with the penalty of duplicating allocated memory.
byte[] byteArrayOut = outputMemStream.ToArray();
// GetBuffer returns a raw buffer raw and so you need to account for the true length yourself.
byte[] byteArrayOut = outputMemStream.GetBuffer();
long len = outputMemStream.Length;
}
More samples can be found here.
If you can live without password functionality, one can mention ZipStorer or the built in .NET function in System.IO.Compression.
I have a task at hand that requires deriving key material using the key derivation function described in NIST SP 800-56A, section 5.8.1. I'm not an expert in Cryptography so please excuse me if the question is naive. Here's what I've done so far:
I have the other party's public key and my private key
Now I try to generate the shared secret using ECDH 1.3.132.1.12 using C# (.NET 4) ECDiffieHellmanCng class like so:
// The GetCngKey method reads the private key from a certificate in my Personal certificate store
CngKey cngPrivateKey = GetCngKey();
ECDiffieHellmanCng ecDiffieHellmanCng = new ECDiffieHellmanCng(cngPrivateKey);
ecDiffieHellmanCng.HashAlgorithm = CngAlgorithm.ECDiffieHellmanP256;
ecDiffieHellmanCng.KeyDerivationFunction = ?? // What do I set here
Finally do this:
ecDiffieHellmanCng.DeriveKeyMaterial(otherPartyPublicKey:);
Where/how do I set the other parameters Algorithm ID, Party U Info, Party V Info?
EDIT
I am open to using other libraries like Bouncy Castle (provided they can be called from .NET)
TL;DR; I haven't found a way to derive the symmetric key using KDF described in NIST SP 800-56A, section 5.8.1 using built-in classes in .NET 4.0 alone
The good news (for me :-)) is that it IS possible in .NET 4.0 using the lovely BouncyCastle library (NuGet: Install-Package BouncyCastle-Ext -Version "1.7.0"). Here's how:
STEP 1: Get other party's public key
Depending on your scenario, this may be read from a certificate or come to you as part of the message containing the encrypted data. Once you have the Base64 encoded public-key, read it into a Org.BouncyCastle.Crypto.Parameters.ECPublicKeyParameters object like so:
var publicKeyBytes = Convert.FromBase64String(base64PubKeyStr);
ECPublicKeyParameters otherPartyPublicKey = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
STEP 2: Read your private-key
This would most-commonly involve reading the private key from a PFX/P12 certificate. The windows account running the code should have access to the PFX/P12 and additionally, if the certificate is imported into a certificate store, you'll need to grant permissions via the All Tasks -> manage private key menu in certmgr.msc
using (StreamReader reader = new StreamReader(path))
{
var fs = reader.BaseStream;
string password = "<password for the PFX>";
Pkcs12Store store = new Pkcs12Store(fs, passWord.ToCharArray());
foreach (string n in store.Aliases)
{
if (store.IsKeyEntry(n))
{
AsymmetricKeyEntry asymmetricKey = store.GetKey(n);
if (asymmetricKey.Key.IsPrivate)
{
ECPrivateKeyParameters privateKey = asymmetricKey.Key as ECPrivateKeyParameters;
}
}
}
}
STEP 3: Compute the shared secret
IBasicAgreement aKeyAgree = AgreementUtilities.GetBasicAgreement("ECDH");
aKeyAgree.Init(privateKey);
BigInteger sharedSecret = aKeyAgree.CalculateAgreement(otherPartyPublicKey);
byte[] sharedSecretBytes = sharedSecret.ToByteArray();
STEP 4: Prepare information required to compute symmetric key:
byte[] algorithmId = Encoding.ASCII.GetBytes(("<prependString/Hex>" + "id-aes256-GCM"));
byte[] partyUInfo = Encoding.ASCII.GetBytes("<as-per-agreement>");
byte[] partyVInfo = <as-per-agreement>;
MemoryStream stream = new MemoryStream(algorithmId.Length + partyUInfo.Length + partyVInfo.Length);
var sr = new BinaryWriter(stream);
sr.Write(algorithmId);
sr.Flush();
sr.Write(partyUInfo);
sr.Flush();
sr.Write(partyVInfo);
sr.Flush();
stream.Position = 0;
byte[] keyCalculationInfo = stream.GetBuffer();
STEP 5: Derive the symmetric key
// NOTE: Use the digest/Hash function as per your agreement with the other party
IDigest digest = new Sha256Digest();
byte[] symmetricKey = new byte[digest.GetDigestSize()];
digest.Update((byte)(1 >> 24));
digest.Update((byte)(1 >> 16));
digest.Update((byte)(1 >> 8));
digest.Update((byte)1);
digest.BlockUpdate(sharedSecret, 0, sharedSecret.Length);
digest.BlockUpdate(keyCalculationInfo, 0, keyCalculationInfo.Length);
digest.DoFinal(symmetricKey, 0);
Now you have the symmetric key ready to do the decryption. To perform decryption using AES, BouncyCastle IWrapper can be used. Obtain an IWrapper using Org.BouncyCastle.Security.WrapperUtilities by calling WrapperUtilities.GetWrapper("AES//") e.g. "AES/CBC/PKCS7". This will also depend on the agreement between the two communicating parties.
Initialize the cipher (IWrapper) with symmetric key and initialization vector (IV) and call the Unwrap method to get plain-text bytes. Finally, convert to string literal using the character encoding used (e.g. UTF8/ASCII/Unicode)
And again I ran into trouble while porting a .NET Desktop App to a Windows Store App...
Long story short, I have a ZIP File with an encrypted and signed XML File and a certificate in it. The decryption is working (more or less) but now I have to "unsign" the XML and I'm stuck.
In the .NET App the unsigning is done with System.Security.Cryptography.Pkcs.SignedCms but that class does not exist in WinRt (as always...)
Is there any alternative in WinRT?
here is some of the code used in the .NET App:
public static byte[] CheckAndRemoveSignature(byte[] data, X509Certificate2Collection certStore, out SignedCms out_signature)
{
SignedCms signedMessage = new SignedCms();
signedMessage.Decode(data);
if ((certStore != null) && (certStore.Count > 0))
signedMessage.CheckSignature(certStore, true);
else
signedMessage.CheckSignature(true);
out_signature = signedMessage;
// return data without signature
return signedMessage.ContentInfo.Content;
}
I already searched a lot, but the only thing I found, that could have helped me was this post. Unfortunately the marked answer does not provide any helpful information :(
Windows 8 Metro cryptography - using SignedCms Pkcs7
I would really appreciate some help here :)
Edit
The essential problem is to get the original xml data out of the signed byte array.
Or, more specifically I need the functionality of these few lines of code in WinRT
SignedCms signedMessage = new SignedCms();
signedMessage.Decode(data);
byte[] result = signedMessage.ContentInfo.Content;
I tried the example from pepo, but I get a MalformedContent Exception
private byte[] CheckAndRemoveSignature(byte[] data)
{
try
{
// load using bouncyCastle
CmsSignedData sig = new CmsSignedData(data);
// var allSigsValid = VerifySignatures(sig);
byte[] content = sig.SignedContent.GetContent() as byte[];
return content;
}
catch (Exception ex)
{
cryptOutput.Text += "Error removing Signature: " + ex;
return data;
}
I get this exception:
Org.BouncyCastle.Cms.CmsException: Malformed content. ---> System.ArgumentException: unknown object in factory: DerApplicationSpecific
at Org.BouncyCastle.Asn1.Cms.ContentInfo.GetInstance(Object obj)
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Asn1InputStream aIn)
--- End of inner exception stack trace ---
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Asn1InputStream aIn)
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Stream input)
at Org.BouncyCastle.Cms.CmsSignedData..ctor(Byte[] sigBlock)
at TestApp.MainPage.CheckAndRemoveSignature(Byte[] data)
Code from Desktop App where the XML-File is signed:
private byte[] signInternal(byte[] data, X509Certificate2 signatureCert, bool signatureOnly)
{
CAPICOM.SignedData signedData = new CAPICOM.SignedDataClass();
CAPICOM.Utilities u = new CAPICOM.UtilitiesClass();
signedData.set_Content(u.ByteArrayToBinaryString(data));
GC.Collect();
CAPICOM.Signer signer = new CAPICOM.Signer();
signer.Options = CAPICOM.CAPICOM_CERTIFICATE_INCLUDE_OPTION.CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY;
CAPICOM.CertificateClass certClass = new CAPICOM.CertificateClass();
certClass.Import(Convert.ToBase64String(signatureCert.Export(X509ContentType.SerializedCert)));
signer.Certificate = certClass;
GC.Collect();
if (this.validateCert(signatureCert))
return (byte[])Convert.FromBase64String(signedData.Sign(signer, signatureOnly, CAPICOM.CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64));
else
return new byte[] { };
}
Solution
In the end it turned out that there was a big problem with encoding, that I did not think was noteworthy. The answer from pepo is working, however I will post my version, to show how it works if you get the file from a zip folder:
// get bytes from zip
byte[] data = getFileContentAsByteArray(zipBytes, ze.FileName);
var dataString = Encoding.UTF8.GetString(data, 0, data.Length);
// check and remove signature
bool isValid;
byte[] withoutSig = CheckAndRemoveSignature(dataString, out isValid);
private byte[] CheckAndRemoveSignature(string data, out bool isValid)
{
isValid = false;
// using bouncyCastle
try
{
var bytes = Convert.FromBase64String(data);
// assign data to CmsSignedData
CmsSignedData sig = new CmsSignedData(bytes);
// check if signature is valid
var allSigsValid = VerifySignaturesBC(sig);
if (allSigsValid.Equals(true)) { isValid = true; }
// get signature from cms
byte[] content = sig.SignedContent.GetContent() as byte[];
return content;
}
catch (Exception ex) { cryptOutput.Text += "Error in 'BouncyCastle unsign' " + ex; return null; }
}
Based on comments I understand that you have a PKCS#7 structure (SignedCms) and the content of that structure is XmlDocument.
Because there is no SignedCms in WinRT API you have two choices. Either use some ASN.1 library and parse PKCS#7 manually looking for content or use i.e. BouncyCastle which has SignedCms implemented and can parse that strusture.
You asked for an example using bouncyCastle. Here it is.
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.X509.Store;
using System.Collections;
using System.Security.Cryptography.Pkcs;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// make some pkcs7 signedCms to work on
SignedCms p7 = new SignedCms(new System.Security.Cryptography.Pkcs.ContentInfo(new byte[] { 0x01, 0x02 }));
p7.ComputeSignature(new CmsSigner(), false);
// encode to get signedCms byte[] representation
var signedCms = p7.Encode();
// load using bouncyCastle
CmsSignedData sig = new CmsSignedData(signedCms);
var allSigsValid = VerifySignatures(sig);
byte[] content = sig.SignedContent.GetContent() as byte[];
}
// taken from bouncy castle SignedDataTest.cs
private static bool VerifySignatures(
CmsSignedData sp)
{
var signaturesValid = true;
IX509Store x509Certs = sp.GetCertificates("Collection");
SignerInformationStore signers = sp.GetSignerInfos();
foreach (SignerInformation signer in signers.GetSigners())
{
ICollection certCollection = x509Certs.GetMatches(signer.SignerID);
IEnumerator certEnum = certCollection.GetEnumerator();
certEnum.MoveNext();
Org.BouncyCastle.X509.X509Certificate cert = (Org.BouncyCastle.X509.X509Certificate)certEnum.Current;
signaturesValid &= signer.Verify(cert);
}
return signaturesValid;
}
}
}
As for the ASN.1 library, I have only worked with bouncyCastle which has ASN.1 parser or ASN.1 editor which is a very useful GUI application for showing structure of PKCS#7, certificates etc. So I can recommend only those two.
You’re probably looking for something like Windows.Security.Cryptography.Certificates.CmsAttachedSignature.VerifySignature() and it's 'Content' property
See here
I've got the following code sample in Java, and I need to re-enact it in C#:
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pkcs8PrivateKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);
Signature sign = Signature.getInstance("MD5withRSA");
sign.initSign(privKey);
sign.update(data);
byte[] signature = sign.sign();
Is it possible with the standard .Net Crypto API, or should I use BouncyCastle?
Thanks,
b.
Another way is to use CNG (Cryptography Next Generation), along with the Security.Cryptography DLL from CodePlex
Then you can write:
byte[] dataToSign = Encoding.UTF8.GetBytes("Data to sign");
using (CngKey signingKey = CngKey.Import(pkcs8PrivateKey, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSACng rsa = new RSACng(signingKey))
{
rsa.SignatureHashAlgorithm = CngAlgorithm.MD5;
return rsa.SignData(dataToSign);
}
Updated thanks to Simon Mourier: with .Net 4.6, you no longer need a separate library
I am running into a very similar problem trying to create a native C# tool for packing Chrome extensions (using SHA1, not MD5, but that's not a big difference). I believe I have tried literally every possible solution for .Net: System.Security.Cryptography, BouncyCastle, OpenSSL.Net and Chilkat RSA.
The best solution is probably Chilkat; their interface is the cleanest and most straightforward, it's well-supported and well-documented, and there are a million examples. For instance, here's some code using their library that does something very close to what you want: http://www.example-code.com/csharp/rsa_signPkcs8.asp. However, it's not free (though $150 is not unreasonable, seeing as I have burned 2 days trying to figure this out, and I make a bit more than $75 a day!).
As a free alternative, JavaScience offers up a number of crypto utilities in source form for multiple languages (including C#/.Net) at http://www.jensign.com/JavaScience/cryptoutils/index.html. The one that's most salient to what you are trying to do is opensslkey (http://www.jensign.com/opensslkey/index.html), which will let you generate a RSACryptoServiceProvider from a .pem file. You can then use that provider to sign your code:
string pemContents = new StreamReader("pkcs8privatekey.pem").ReadToEnd();
var der = opensslkey.DecodePkcs8PrivateKey(pemContents);
RSACryptoServiceProvider rsa = opensslkey.DecodePrivateKeyInfo(der);
signature = rsa.SignData(data, new MD5CryptoServiceProvider());
You can use this code . At the first you should download "BouncyCastle.Crypto.dll" from http://www.bouncycastle.org/csharp/ .
/// <summary>
/// MD5withRSA Signing
/// https://www.vrast.cn
/// keyle_xiao 2017.1.12
/// </summary>
public class MD5withRSASigning
{
public Encoding encoding = Encoding.UTF8;
public string SignerSymbol = "MD5withRSA";
public MD5withRSASigning() { }
public MD5withRSASigning(Encoding e, string s)
{
encoding = e;
SignerSymbol = s;
}
private AsymmetricKeyParameter CreateKEY(bool isPrivate, string key)
{
byte[] keyInfoByte = Convert.FromBase64String(key);
if (isPrivate)
return PrivateKeyFactory.CreateKey(keyInfoByte);
else
return PublicKeyFactory.CreateKey(keyInfoByte);
}
public string Sign(string content, string privatekey)
{
ISigner sig = SignerUtilities.GetSigner(SignerSymbol);
sig.Init(true, CreateKEY(true, privatekey));
var bytes = encoding.GetBytes(content);
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
/* Base 64 encode the sig so its 8-bit clean */
var signedString = Convert.ToBase64String(signature);
return signedString;
}
public bool Verify(string content, string signData, string publickey)
{
ISigner signer = SignerUtilities.GetSigner(SignerSymbol);
signer.Init(false, CreateKEY(false, publickey));
var expectedSig = Convert.FromBase64String(signData);
/* Get the bytes to be signed from the string */
var msgBytes = encoding.GetBytes(content);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
return signer.VerifySignature(expectedSig);
}
}
This SO question answers the PKCS#8 part of your code. The rest of the .NET RSA classes are a bizarre jumble of partially overlapping classes that are very difficult to fathom. It certainly appears that signature support is in either of the RSACryptoServiceProvider and/or RSAPKCS1SignatureFormatter classes.
Disclaimer: I know Java and cryptography, but my knowledge of C# and .NET is very limited. I am writing here only under the influence of my Google-fu skills.
Assuming that you could decode a PKCS#8-encoded RSA private key, then, from what I read on MSDN, the rest of the code should look like this:
byte[] hv = MD5.Create().ComputeHash(data);
RSACryptoServiceProvider rsp = new RSACryptoServiceProvider();
RSAParameters rsp = new RSAParameters();
// here fill rsp fields by decoding pkcs8PrivateKey
rsp.ImportParameters(key);
RSAPKCS1SignatureFormatter rf = new RSAPKCS1SignatureFormatter(rsp);
rf.SetHashAlgorithm("MD5");
byte[] signature = rf.CreateSignature(hv);
The relevant classes are in the System.Security.Cryptography namespace.
As for the PKCS#8 key blob decoding (i.e. filling in the rsp fields), I found this page which describes a command-line utility in C# which can perform that job. The source code is provided and is a single C# file. From what I read in it, that code decodes the PKCS#8 file "manually"; indirectly, this should mean that raw .NET (2.0) does not have facilities for PKCS#8 key file decoding (otherwise the author of that tool would not have went to the trouble of implementing such decoding). For your task at hand, you could scavenge from that source file the parts that you need, skipping anything about PEM and symmetric encryption; your entry point would be the DecodePrivateKeyInfo() function, which apparently expects a DER-encoded unencrypted PKCS#8 file, just like Java's PKCS8EncodedKeySpec.