//******************************************************* // DecEnvelop // Enveloped File Decryptor // // Copyright (C) 2003. Michel I. Gallant //******************************************************** // // DecEnvelop.cs // // Uses P/Invoke to CryptoAPI simplified message function // CryptDecryptMessage() // Input file can be binary or b64 CMS/pkcs#7 format //******************************************************** using System; using System.IO; using System.Runtime.InteropServices; using System.ComponentModel; using System.Reflection; using System.Text; namespace JavaScience { public class Win32 { [DllImport("crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern IntPtr CertOpenSystemStore( IntPtr hCryptProv, string storename) ; [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CertCloseStore( IntPtr hCertStore, uint dwFlags) ; [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CryptDecryptMessage( ref CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, byte[] pbEncryptedBlob, int cbEncryptedBlob, [In, Out] byte[] pbDecrypted, ref int pcbDecrypted, IntPtr ppXchgCert); } [StructLayout(LayoutKind.Sequential)] public struct CRYPT_DECRYPT_MESSAGE_PARA { public int cbSize; public uint dwMsgAndCertEncodingType; public int cCertStore; public IntPtr rghCertStore; //public uint dwFlags; } //--Note: CRYPT_DECRYPT_MESSAGE_PARA.dwFlags only for XP+ OS public class DecEnvelop { const string MY = "MY"; const string OTHERS = "AddressBook"; const uint PKCS_7_ASN_ENCODING = 0x00010000; const uint X509_ASN_ENCODING = 0x00000001; static uint MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING ; static bool verbose = false; public static void Main(String[] args){ String sysstore = MY; IntPtr hSysStore = IntPtr.Zero; IntPtr hCertCntxt = IntPtr.Zero; IntPtr pProvInfo = IntPtr.Zero; IntPtr pexcKey = IntPtr.Zero; String filename = null; String outfile = null; if(args.Length < 2){ Usage(); return; } filename = args[0]; outfile = args[1]; if(!File.Exists(filename)){ Console.WriteLine("File {0} not found. ", filename); return; } if(File.Exists(outfile)){ Console.Write("File {0} already exists.\nOverwrite output file? ", outfile); string resp = Console.ReadLine().Trim().ToLower(); if( !(resp.Equals("y") || resp.Equals("yes")) ) return; } //------- Get encoded enveloped data from file ----- byte[] envdata = DecEnvelop.GetFileBytes(filename); byte[] denvbytes; if(verbose) Console.WriteLine("File has {0} bytes",envdata.Length); hSysStore = Win32.CertOpenSystemStore(IntPtr.Zero, sysstore) ; if(hSysStore == IntPtr.Zero) { Console.WriteLine("Couldn't get system store {0}", sysstore); return; } if (verbose) Console.WriteLine("Store Handle:\t{0}", hSysStore); CRYPT_DECRYPT_MESSAGE_PARA messpara = new CRYPT_DECRYPT_MESSAGE_PARA(); messpara.cbSize = Marshal.SizeOf(messpara); messpara.dwMsgAndCertEncodingType = MY_ENCODING_TYPE; messpara.cCertStore = 1; messpara.rghCertStore = Marshal.AllocHGlobal(IntPtr.Size); //one cert store pointer Marshal.WriteIntPtr(messpara.rghCertStore, hSysStore); int decbytes = 0; //------ if fails, then might be b64 encoded file; try b64 decoding ------------ if(!Win32.CryptDecryptMessage( ref messpara, envdata, envdata.Length, null, ref decbytes, IntPtr.Zero)) { ASCIIEncoding ascii = new ASCIIEncoding(); String datastr = ascii.GetString(envdata); try{ envdata = Convert.FromBase64String(datastr); //try to b64 decode } catch(System.FormatException){ Console.WriteLine("File '{0}' cannot be decrypted", filename); Marshal.FreeHGlobal(messpara.rghCertStore); return; } if(verbose) Console.WriteLine("Base64 converted size: {0}", envdata.Length); } if(Win32.CryptDecryptMessage( ref messpara, envdata, envdata.Length, null, ref decbytes, IntPtr.Zero)) { if(verbose) Console.WriteLine("Bytes allocated: {0}", decbytes); denvbytes = new byte[decbytes]; if(Win32.CryptDecryptMessage(ref messpara, envdata, envdata.Length, denvbytes, ref decbytes, IntPtr.Zero)) { Console.WriteLine("Decrypted data: {0} bytes", decbytes); PutFileBytes(outfile, denvbytes, decbytes); if(verbose) DecEnvelop.DisplayBytes(denvbytes); } else Console.WriteLine("Failed to decrypt file (incorrect password?)"); } else{ Console.WriteLine("Couldn't decrypt file") ; showWin32Error(Marshal.GetLastWin32Error()); } Marshal.FreeHGlobal(messpara.rghCertStore); if(hSysStore != IntPtr.Zero) Win32.CertCloseStore(hSysStore, 0) ; } 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 void DisplayBytes(byte[] data) { for(int i=1; i<=data.Length; i++){ Console.Write("{0:x2} ", data[i-1]) ; if(i%16 == 0) Console.WriteLine(""); } Console.WriteLine(); } private static void PutFileBytes(String outfile, byte[] data, int bytes) { FileStream fs = null; if(bytes > data.Length) { Console.WriteLine("Too many bytes"); return; } try{ fs = new FileStream(outfile, FileMode.Create); fs.Write(data, 0, bytes); Console.WriteLine("Wrote file '{0}'", outfile) ; } catch(Exception e) { Console.WriteLine(e.Message) ; } finally { fs.Close(); } } private static void Usage() { Console.WriteLine("\nUsage:\nDecEnvelop "); } private static void showWin32Error(int errorcode){ Win32Exception myEx=new Win32Exception(errorcode); Console.WriteLine("Error code:\t 0x{0:X}", myEx.ErrorCode); Console.WriteLine("Error message:\t " + myEx.Message); } } }