Introduction
This portion of code covers the signing of original string and get the certificate data from Mexico FIEL (package of .key file, .cer file and password for .key file) to build electronic invoices. Also, can be used with other generic purposes, but it only covers encryption of Mexico files. to check other encryption formats, you can check the full code on which this implementation is based at http://www.jensign.com/opensslkey/opensslkey.cs
Background
I have a nightmare finding correct functions to get the electronic invoices signed, many developers recommend to use openssl.exe to obtain the signed string for electronic invoices, that option has the inconvenience of compatibility between 32b and 64b operating systems, and requires a separate setup. This piece of code cover that specific issue. Note to my fellow countrymen, this code does not generate the original string (cadena original) nor the XML file.
Using the code
For specific implentation mentioned, you only need to use
the SignString(string pKeyFile,string pPassword,string OriginalString) function, on which
pKeyFile=fullpath .key file
pPassword=password of .key file
OriginalString=string to be signed.
Function returns signed string according with the parameters
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics;
using System.ComponentModel;
namespace OpenSSL
{
public class opensslkey
{
public string SignString(string pKeyFile,string pPassword,string OriginalString)
{
string SignedString = "";
string filename = pKeyFile;
if (!File.Exists(filename))
{
return ".key file does not exist " + pKeyFile;
}
RSACryptoServiceProvider rsa=OpenKeyFile(filename, pPassword);
if (rsa != null)
{
byte[] CO=Encoding.UTF8.GetBytes(OriginalString);
byte[] SignedBytes=rsa.SignData(CO, new SHA1CryptoServiceProvider());
SignedString = Convert.ToBase64String(SignedBytes);
}
return SignedString;
}
public RSACryptoServiceProvider OpenKeyFile(String filename,string pPassword)
{
RSACryptoServiceProvider rsa = null;
byte[] keyblob = GetFileBytes(filename);
if (keyblob == null)
return null;
rsa = DecodePrivateKeyInfo(keyblob, pPassword);
if (rsa != null)
{
return rsa;
}
return null;
}
public static RSACryptoServiceProvider
DecodePrivateKeyInfo(byte[] encpkcs8,string pPassword)
{
byte[] OIDpkcs5PBES2 = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D };
byte[] OIDpkcs5PBKDF2 = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C };
byte[] OIDdesEDE3CBC = { 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x07 };
byte[] seqdes = new byte[10];
byte[] seq = new byte[11];
byte[] salt;
byte[] IV;
byte[] encryptedpkcs8;
byte[] pkcs8;
int saltsize, ivsize, encblobsize;
int iterations;
MemoryStream mem = new MemoryStream(encpkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem);
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
seq = binr.ReadBytes(11);
if (!CompareBytearrays(seq, OIDpkcs5PBES2))
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
seq = binr.ReadBytes(11);
if (!CompareBytearrays(seq, OIDpkcs5PBKDF2))
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
bt = binr.ReadByte();
if (bt != 0x04)
return null;
saltsize = binr.ReadByte();
salt = binr.ReadBytes(saltsize);
bt = binr.ReadByte();
if (bt != 0x02)
return null;
int itbytes = binr.ReadByte();
if (itbytes == 1)
iterations = binr.ReadByte();
else if (itbytes == 2)
iterations = 256 * binr.ReadByte() + binr.ReadByte();
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
seqdes = binr.ReadBytes(10);
if (!CompareBytearrays(seqdes, OIDdesEDE3CBC))
return null;
bt = binr.ReadByte();
if (bt != 0x04)
return null;
ivsize = binr.ReadByte();
IV = binr.ReadBytes(ivsize);
bt = binr.ReadByte();
if (bt != 0x04)
return null;
bt = binr.ReadByte();
if (bt == 0x81)
encblobsize = binr.ReadByte();
else if (bt == 0x82)
encblobsize = 256 * binr.ReadByte() + binr.ReadByte();
else
encblobsize = bt;
encryptedpkcs8 = binr.ReadBytes(encblobsize);
SecureString secpswd = new SecureString();
foreach (char c in pPassword)
secpswd.AppendChar(c);
pkcs8 = DecryptPBDK2(encryptedpkcs8, salt, IV, secpswd, iterations);
if (pkcs8 == null)
return null;
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8);
return rsa;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
public void CertificateData(string pCerFile, out string Certificate, out string CertificateNumber)
{
X509Certificate cert = new X509Certificate(pCerFile);
byte[] strcert = cert.GetRawCertData();
Certificate = Convert.ToBase64String(strcert);
strcert = cert.GetSerialNumber();
CertificateNumber = Reverse(System.Text.Encoding.UTF8.GetString(strcert));
}
public string Reverse(string Original)
{
string Reverse = "";
for (int i = Original.Length - 1; i >= 0; i--)
Reverse += Original.Substring(i, 1);
return Reverse;
}
private static byte[] GetFileBytes(String filename)
{
if (!File.Exists(filename))
return null;
Stream stream = new FileStream(filename, FileMode.Open);
int datalen = (int)stream.Length;
byte[] filebytes = new byte[datalen];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(filebytes, 0, datalen);
stream.Close();
return filebytes;
}
private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
public static byte[] DecryptPBDK2(byte[] edata, byte[] salt,
byte[] IV, SecureString secpswd, int iterations)
{
CryptoStream decrypt = null;
IntPtr unmanagedPswd = IntPtr.Zero;
byte[] psbytes = new byte[secpswd.Length];
unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length);
Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
try
{
Rfc2898DeriveBytes kd = new Rfc2898DeriveBytes(psbytes, salt, iterations);
TripleDES decAlg = TripleDES.Create();
decAlg.Key = kd.GetBytes(24);
decAlg.IV = IV;
MemoryStream memstr = new MemoryStream();
decrypt = new CryptoStream(memstr, decAlg.CreateDecryptor(), CryptoStreamMode.Write);
decrypt.Write(edata, 0, edata.Length);
decrypt.Flush();
decrypt.Close();
byte[] cleartext = memstr.ToArray();
return cleartext;
}
catch (Exception e)
{
Console.WriteLine("Problem decrypting: {0}", e.Message);
return null;
}
}
public static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
{
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
MemoryStream mem = new MemoryStream(pkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem);
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
else
return null;
bt = binr.ReadByte();
if (bt != 0x02)
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0001)
return null;
seq = binr.ReadBytes(15);
if (!CompareBytearrays(seq, SeqOID))
return null;
bt = binr.ReadByte();
if (bt != 0x04)
return null;
bt = binr.ReadByte();
if (bt == 0x81)
binr.ReadByte();
else
if (bt == 0x82)
binr.ReadUInt16();
byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
return rsacsp;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem);
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102)
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
Console.WriteLine("showing components ..");
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02)
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte();
else
if (bt == 0x82)
{
highbyte = binr.ReadByte();
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt;
}
while (binr.ReadByte() == 0x00)
{
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
}
}
public void Test()
{
OpenSSL.opensslkey libssl = new OpenSSL.opensslkey();
string SignedString = libssl.SignString(@"c:\fiel\aaaa121213123123aaa_t.key",
"0123456789", "||3.2|test|cadena|original|");
string Certificate = "";
string CertificateNumber = "";
libssl.CertificateData(@"c:\fiel\aaaa121213123123aaa_t.cer", out Certificate, out CertificateNumber);
Certificate ="CgfhC02gAwIBAgIUMDAwMDEwM7AwMDAyMD9Iyjg3zQwDYJKoIhcNAQEFBQAwggGVMTgwNgYDVQQDDC9BLkMuIGRlbCBTZXJ2aWNpbyBkZSBBZG1pbmlzdHJhY2nDs24gVHJpYnV0YXJpYTEvMC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3RyYWNpw7NuIFRyaWJ1dGFyaWExODA2BgNVBAsML0FkbWluaXN0cmFjacOzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5mb3JtYWNpw7NuMSEwHwYJKoZIhvcNAQkBFhJhc2lzbmV0QHNhdC5nb2IubXgxJjAkBgNVBAkMHUF2LiBIaWRhbGdvIDc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDELMAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxFDASBgNVBAcMC0N1YXVodMOpbW9jMRUwEwYDVQQtEwxTQVQ5NzA3MDFOTjMxPjA8BgkqhkiG9w0BCQIML1Jlc3BvbnNhYmxlOiBDZWNpbGlhIEd1aWxsZXJtaW5hIEdhcmPDrWEgR3VlcnJhMB4XDTEyMTEwMzEzMDAzN1oXDTE2MTEwMzEzMDAzN1owgaYxHjAcBgNVBAMTFURBTUlBTiBQRVJFWiBNQVJUSU5FWjEeMBwGA1UEKRMVREFNSUFOIFBFUkVaIE1BUlRJTkVaMR4wHAYDVQQKExVEQU1JQU4gUEVSRVogTUFSVElORVoxFjAUBgNVBC0TDVBFTUQ3MzA3MjQyMTQxGzAZBgNVBAUTElBFTUQ3MzA3MjRIVFNSUk0wOTEPMA0GA1UECxMGVW5pZGFkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyvvaw+0P7kMbpzMiYG7IrX/lb3tJkvDIAgWCnIid2mdyjaPrMVi/2aZrohvnUZjaYUlbxFWhLBJcXEdsZpT3QAF+JahWqIzkg2PLk5MHDdncOJI+s/26yNs+efjOlWXa2uS8Q+9M9ZxOWLOMX6sLSTupdR8pvvgylQXjMm3L6RQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQUFAAOCAQEATOqpxj3d1iFJx/OG4Jlg8WYG7JJiGK8b0Yi7Gh7YXf03cJQBLsbEj UEunO04s3Tl/gwoglrQ/5uvYeswoin4zq6Sj6MOFb9mFUfDRT8p9VKm19dW/IWjFfgVgaS5OWoMK3X0/EkrnVdz7nb2q0f5/icbUbSvNu/6wBo6FS53qqHNL3DF0sK4KbUJCgrpmdv0/0Rp8M8ezYNEqqNMf/tqHN9EV5hf02M7z1pyL6Dg0Aj4NkWTdWDqWe/fLTeNcUFNDJCZFBMTn83t4z8G9aRHRlNepepSeRX5AvQc6W+iTclU0GM33w=="
CertificateNumber ="00009000000822643513"
*/
}