lib/powershell-pulp-main.cs
using System;
using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Diagnostics; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; namespace PowershellPulp { // From: https://www.codeproject.com/Articles/162194/Certificates-to-DB-and-Back?msg=4339068 public enum PemStringType { Certificate, RsaPrivateKey } public class Helpers { // This helper function parses an integer size from the reader using the // ASN.1 format public static int DecodeIntegerSize(System.IO.BinaryReader rd) { byte byteValue; int count; byteValue = rd.ReadByte(); if (byteValue != 0x02) // Indicates an ASN.1 integer value follows return 0; byteValue = rd.ReadByte(); if (byteValue == 0x81) { count = rd.ReadByte(); // Data size is the following byte } else if (byteValue == 0x82) { byte hi = rd.ReadByte(); // Data size in next 2 bytes byte lo = rd.ReadByte(); count = BitConverter.ToUInt16(new[] { lo, hi }, 0); } else { count = byteValue; // We already have the data size } // Remove high order zeros in data while (rd.ReadByte() == 0x00) { count -= 1; } rd.BaseStream.Seek(-1, System.IO.SeekOrigin.Current); return count; } public static byte[] GetBytesFromPEM(string pemString, PemStringType type) { string header; string footer; switch (type) { case PemStringType.Certificate: header = "-----BEGIN CERTIFICATE-----"; footer = "-----END CERTIFICATE-----"; break; case PemStringType.RsaPrivateKey: header = "-----BEGIN RSA PRIVATE KEY-----"; footer = "-----END RSA PRIVATE KEY-----"; break; default: return null; } int start = pemString.IndexOf(header) + header.Length; int end = pemString.IndexOf(footer, start) - start; return Convert.FromBase64String(pemString.Substring(start, end)); } public static byte[] AlignBytes(byte[] inputBytes, int alignSize) { int inputBytesSize = inputBytes.Length; if ((alignSize != -1) && (inputBytesSize < alignSize)) { byte[] buf = new byte[alignSize]; for (int i = 0; i < inputBytesSize; ++i) { buf[i + (alignSize - inputBytesSize)] = inputBytes[i]; } return buf; } else { return inputBytes; // Already aligned, or doesn't need alignment } } } internal class RSAParameterTraits { public RSAParameterTraits(int modulusLengthInBits) { /* The modulus length is supposed to be one of the common lengths, which is the commonly referred to strength of the key, like 1024 bit, 2048 bit, etc. It might be a few bits off though, since if the modulus has leading zeros it could show up as 1016 bits or something like that. */ int assumedLength = -1; double logbase = Math.Log(modulusLengthInBits, 2); if (logbase == (int)logbase) { // It's already an even power of 2 assumedLength = modulusLengthInBits; } else { // It's not an even power of 2, so round it up to the nearest // power of 2. assumedLength = (int)(logbase + 1.0); assumedLength = (int)(Math.Pow(2, assumedLength)); // Can this really happen in the field? I've never seen it, so if it // happens you should verify that this really does the 'right' thing! System.Diagnostics.Debug.Assert(false); } switch (assumedLength) { case 1024: this.size_Mod = 0x80; this.size_Exp = -1; this.size_D = 0x80; this.size_P = 0x40; this.size_Q = 0x40; this.size_DP = 0x40; this.size_DQ = 0x40; this.size_InvQ = 0x40; break; case 2048: this.size_Mod = 0x100; this.size_Exp = -1; this.size_D = 0x100; this.size_P = 0x80; this.size_Q = 0x80; this.size_DP = 0x80; this.size_DQ = 0x80; this.size_InvQ = 0x80; break; case 4096: this.size_Mod = 0x200; this.size_Exp = -1; this.size_D = 0x200; this.size_P = 0x100; this.size_Q = 0x100; this.size_DP = 0x100; this.size_DQ = 0x100; this.size_InvQ = 0x100; break; default: System.Diagnostics.Debug.Assert(false); // Unknown key size? break; } } public int size_Mod = -1; public int size_Exp = -1; public int size_D = -1; public int size_P = -1; public int size_Q = -1; public int size_DP = -1; public int size_DQ = -1; public int size_InvQ = -1; } public class Crypto { /* This helper function parses an RSA private key using the ASN.1 format - takes a Byte array containing PEM string of private key - returns an instance of RSACryptoServiceProvider representing the requested private key - null if method fails on retriving the key. */ public static RSACryptoServiceProvider DecodeRsaPrivateKey(byte[] privateKeyBytes) { MemoryStream ms = new MemoryStream(privateKeyBytes); BinaryReader rd = new BinaryReader(ms); try { byte byteValue; ushort shortValue; shortValue = rd.ReadUInt16(); switch (shortValue) { case 0x8130: // If true, data is little endian since the proper logical seq is // 0x30 0x81 rd.ReadByte(); // Advance 1 byte break; case 0x8230: rd.ReadInt16(); // Advance 2 bytes break; default: Debug.Assert(false); // Improper ASN.1 format return null; } shortValue = rd.ReadUInt16(); if (shortValue != 0x0102) { // Version number Debug.Assert(false); // Improper ASN.1 format, unexpected version number return null; } byteValue = rd.ReadByte(); if (byteValue != 0x00) { Debug.Assert(false); // Improper ASN.1 format return null; } /* The data following the version will be the ASN.1 data itself, which in our case are a sequence of integers. In order to solve a problem with instancing RSACryptoServiceProvider via default constructor on .net 4.0 this is a hack */ CspParameters parms = new CspParameters(); parms.Flags = CspProviderFlags.NoFlags; parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant(); parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms); RSAParameters rsAparams = new RSAParameters(); rsAparams.Modulus = rd.ReadBytes(Helpers.DecodeIntegerSize(rd)); /* Argh, this is a pain. From emperical testing it appears to be that RSAParameters doesn't like byte buffers that have their leading zeros removed. The RFC doesn't address this area that I can see, so it's hard to say that this is a bug, but it sure would be helpful if it allowed that. So, there's some extra code here that knows what the sizes of the various components are supposed to be. Using these sizes we can ensure the buffer sizes are exactly what the RSAParameters expect. Thanks, Microsoft. */ RSAParameterTraits traits = new RSAParameterTraits(rsAparams.Modulus.Length * 8); rsAparams.Modulus = Helpers.AlignBytes(rsAparams.Modulus, traits.size_Mod); rsAparams.Exponent = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Exp); rsAparams.D = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_D); rsAparams.P = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_P); rsAparams.Q = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_Q); rsAparams.DP = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DP); rsAparams.DQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_DQ); rsAparams.InverseQ = Helpers.AlignBytes(rd.ReadBytes(Helpers.DecodeIntegerSize(rd)), traits.size_InvQ); rsa.ImportParameters(rsAparams); return rsa; } catch (Exception) { Debug.Assert(false); return null; } finally { rd.Close(); } } } // From https://github.com/csob/paymentgateway/blob/master/Integration%20Examples/eAPI%20v1.6/dotNET/CsobGTWDemo/CsobGatewayClientExample/Security/Crypto.cs public class Crypto2 { public static System.Security.Cryptography.RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key ------ System.IO.MemoryStream mem = new System.IO.MemoryStream(privkey); System.IO.BinaryReader binr = new System.IO.BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; int elems = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; twobytes = binr.ReadUInt16(); if (twobytes != 0x0102) //version number return null; bt = binr.ReadByte(); if (bt != 0x00) return null; //------ all private key components are Integer sequences ---- 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); // System.Security.Cryptography.RSA.Create(); // ------- create RSACryptoServiceProvider instance and initialize with public key ----- System.Security.Cryptography.RSACryptoServiceProvider RSA = new System.Security.Cryptography.RSACryptoServiceProvider(); System.Security.Cryptography.RSAParameters RSAparams = new System.Security.Cryptography.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(System.IO.BinaryReader binr) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binr.ReadByte(); if (bt != 0x02) //expect integer return 0; bt = binr.ReadByte(); if (bt == 0x81) count = binr.ReadByte(); // data size in next byte else if (bt == 0x82) { highbyte = binr.ReadByte(); // data size in next 2 bytes lowbyte = binr.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else { count = bt; // we already have the data size } while (binr.ReadByte() == 0x00) { //remove high order zeros in data count -= 1; } binr.BaseStream.Seek(-1, System.IO.SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte return count; } 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 class Certificate { public Certificate() { } public Certificate(string cert, string key, string password) { this.PublicCertificate = cert; this.PrivateKey = key; this.Password = password; } #region Fields private string _publicCertificate; private string _privateKey; private string _password; #endregion #region Properties public string PublicCertificate { get { return _publicCertificate; } set { _publicCertificate = value; } } public string PrivateKey { get { return _privateKey; } set { _privateKey = value; } } public string Password { get { return _password; } set { _password = value; } } #endregion public X509Certificate2 GetCertificateFromPEMstring(bool certOnly) { if (certOnly) return GetCertificateFromPEMstring(this.PublicCertificate); else return GetCertificateFromPEMstring(this.PublicCertificate, this.PrivateKey, this.Password); } public static X509Certificate2 GetCertificateFromPEMstring(string publicCert) { return new X509Certificate2(Encoding.UTF8.GetBytes(publicCert)); } public static X509Certificate2 GetCertificateFromPEMstring(string publicCert, string privateKey, string password) { byte[] certBuffer = Helpers.GetBytesFromPEM(publicCert, PemStringType.Certificate); byte[] keyBuffer = Helpers.GetBytesFromPEM(privateKey, PemStringType.RsaPrivateKey); X509Certificate2 certificate = new X509Certificate2(certBuffer, password); RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer); certificate.PrivateKey = prov; return certificate; } } } |