//******************************************************************* // EnvelInfo // // Copyright (C) 2003. Michel I. Gallant // //******************************************************************* // // EnvelInfo.cs // // This C# utility for .NET Framework 1.0/1.1 verifies extracts // enveloped message recipient and algorithm information // //******************************************************************* 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 IntPtr CryptMsgOpenToDecode( uint dwMsgEncodingType, uint dwFlags, uint dwMsgType, IntPtr hCryptProv, IntPtr pRecipientInfo, IntPtr pStreamInfo ); [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CryptMsgUpdate( IntPtr hCryptMsg, byte[] pbData, int cbData, bool fFinal ); [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CryptMsgClose( IntPtr hCryptMsg ); [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CryptMsgGetParam( IntPtr hCryptMsg, uint dwParamType, int dwIndex, IntPtr pvData, ref uint pcbData ); [DllImport("crypt32.dll", SetLastError=true)] public static extern bool CryptMsgGetParam( IntPtr hCryptMsg, uint dwParamType, int dwIndex, ref uint pvData, ref uint pcbData ); [DllImport("crypt32.dll")] public static extern bool CryptDecodeObject( uint CertEncodingType, uint lpszStructType, byte[] pbEncoded, int cbEncoded, uint flags, IntPtr pvStructInfo, ref int cbStructInfo ); [DllImport("crypt32.dll", SetLastError=true)] public static extern IntPtr CryptFindOIDInfo( uint dwKeyType, [MarshalAs(UnmanagedType.LPStr)] String szOID, uint dwGroupId ); } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] public struct CERT_NAME_INFO { public int cRDN; public IntPtr rgRDN; //PCERT_RDN } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] public struct CERT_RDN { public int cRDNAttr; public IntPtr rgRDNAttr; } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] public struct CERT_RDN_ATTR { public IntPtr pszObjId; public int dwValueType; public int cbData; public IntPtr pbData; } [StructLayout(LayoutKind.Sequential)] public struct CRYPT_ALGORITHM_IDENTIFIER{ [MarshalAs(UnmanagedType.LPStr)] public String pszObjID; public uint cbData; //CRYPT_OBJID_BLOB Parameters blob public IntPtr pbData; //CRYPT_OBJID_BLOB Parameters blob } [StructLayout(LayoutKind.Sequential)] public struct CRYPT_RC2_CBC_PARAMETERS { public int dwVersion; public bool fIV; [MarshalAs(UnmanagedType.ByValArray, SizeConst=8)] public byte[] rgbIV; } public class EnvelInfo { const uint PKCS_7_ASN_ENCODING = 0x00010000; const uint X509_ASN_ENCODING = 0x00000001; const uint CMSG_ENVELOPE_ALGORITHM_PARAM = 15; const uint CMSG_RECIPIENT_COUNT_PARAM = 17; const uint CMSG_RECIPIENT_INFO_PARAM = 19; const uint PKCS_RC2_CBC_PARAMETERS = 41; const uint X509_NAME = 7; const uint CRYPT_OID_INFO_OID_KEY = 1; const int CERT_RDN_PRINTABLE_STRING = 4; const int CERT_RDN_TELETEX_STRING = 5; const int CERT_RDN_T61_STRING = 5; const int CERT_RDN_IA5_STRING = 7; const int CERT_RDN_GENERAL_STRING = 10; const int CERT_RDN_UNICODE_STRING = 12; const int CRYPT_RC2_40BIT_VERSION = 160; const int CRYPT_RC2_56BIT_VERSION = 52; const int CRYPT_RC2_64BIT_VERSION = 120; const int CRYPT_RC2_128BIT_VERSION = 58; const string szOID_RSA_RC2CBC = "1.2.840.113549.3.2"; const string szOID_RSA_RC4 = "1.2.840.113549.3.4"; const string szOID_RSA_DES_EDE3_CBC = "1.2.840.113549.3.7"; const string szOID_RSA_RC5_CBCPad = "1.2.840.113549.3.9"; static uint MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING ; public static void Main(String[] args){ String filename = null; IntPtr hMsg = IntPtr.Zero; IntPtr pProvInfo = IntPtr.Zero; IntPtr paramData = IntPtr.Zero; uint paramBytecount = 0; if(args.Length < 1){ Usage(); return; } filename = args[0]; if(!File.Exists(filename)){ Console.WriteLine("File '{0}' not found. ", filename); return; } //------- Get encoded enveloped data from file ----- byte[] envdata = GetFileBytes(filename); Console.WriteLine("File '{0}' ({1} bytes)", filename, envdata.Length); hMsg = Win32.CryptMsgOpenToDecode(MY_ENCODING_TYPE, 0, 0, pProvInfo, IntPtr.Zero, IntPtr.Zero); if(hMsg == IntPtr.Zero){ Console.WriteLine("\nCouldn't open message to decode"); showWin32Error(Marshal.GetLastWin32Error()); return; } if(!Win32.CryptMsgUpdate(hMsg, envdata, envdata.Length, true)){ //message may be b64 encoded if(hMsg != IntPtr.Zero) Win32.CryptMsgClose(hMsg) ; hMsg = Win32.CryptMsgOpenToDecode(MY_ENCODING_TYPE, 0, 0, pProvInfo, IntPtr.Zero, IntPtr.Zero); string b64str = (new ASCIIEncoding()).GetString(envdata); // possibly b64 file envdata = Convert.FromBase64String(b64str); if(!Win32.CryptMsgUpdate(hMsg, envdata, envdata.Length, true)){ Win32.CryptMsgClose(hMsg) ; Console.WriteLine("\nCouldn't update message to decode (probably invalid data)"); showWin32Error(Marshal.GetLastWin32Error());; return; } Console.WriteLine("Base64 encoded enveloped data") ; } //------- Get CMS recipients count for EnvelopedData message ------------- uint recips = 0; paramBytecount = 4; //CMSG_RECIPIENT_COUNT_PARAM returns pdword if(Win32.CryptMsgGetParam(hMsg, CMSG_RECIPIENT_COUNT_PARAM, 0, ref recips, ref paramBytecount)){ Console.WriteLine("Enveloped message has {0} recipients", recips) ; } else showWin32Error(Marshal.GetLastWin32Error()); //------ Get the recipients information ----------- for (int i=0; i0){ byte[] pdata = new byte[cbData]; Marshal.Copy(algID.pbData, pdata, 0, (int)cbData); if( (algID.pszObjID).Equals(szOID_RSA_RC2CBC)) Console.WriteLine("{0} bits", RC2Keysize(pdata)); } Marshal.FreeHGlobal(paramData); Console.WriteLine("\n------------------------------------------"); } else showWin32Error(Marshal.GetLastWin32Error()); if(hMsg != IntPtr.Zero) Win32.CryptMsgClose(hMsg) ; } //---------- Decode asn.1 encoded IssuerName ----------------- private static void DecodeIssuername(byte[] encodedissuer) { IntPtr structinfo = IntPtr.Zero; int cbstruct = 0; if(Win32.CryptDecodeObject(MY_ENCODING_TYPE, X509_NAME, encodedissuer, encodedissuer.Length, 0, IntPtr.Zero, ref cbstruct)) { structinfo = Marshal.AllocHGlobal(cbstruct); Win32.CryptDecodeObject(MY_ENCODING_TYPE, X509_NAME, encodedissuer, encodedissuer.Length, 0, structinfo, ref cbstruct); CERT_NAME_INFO certinfo = (CERT_NAME_INFO) Marshal.PtrToStructure(structinfo, typeof(CERT_NAME_INFO)) ; //Console.WriteLine("Structures: {0}", certinfo.cRDN) ; IntPtr rgrdn = certinfo.rgRDN; for(int j=1; j<=certinfo.cRDN; j++) //loop through array elements (each is a CERT_RDN struct) and advance IntPtr { CERT_RDN certrdn = (CERT_RDN) Marshal.PtrToStructure(rgrdn, typeof(CERT_RDN)) ; //Console.WriteLine("RDN attributes count: {0}", certrdn.cRDNAttr) ; //only take first one. IntPtr rgrdnattr = certrdn.rgRDNAttr; CERT_RDN_ATTR attr = (CERT_RDN_ATTR) Marshal.PtrToStructure(rgrdnattr, typeof(CERT_RDN_ATTR)) ; int valtype = attr.dwValueType ; string szOID = Marshal.PtrToStringAnsi(attr.pszObjId) ; Console.Write("{0} \t{1}=", szOID, OIDName(szOID)) ; if(valtype == CERT_RDN_PRINTABLE_STRING || //if text-displayable attribute valtype == CERT_RDN_IA5_STRING) { byte[] attrvalue = new byte[attr.cbData]; Marshal.Copy(attr.pbData, attrvalue, 0, attrvalue.Length); Console.WriteLine("{0}", (new ASCIIEncoding()).GetString(attrvalue)); } rgrdn = (IntPtr)(4 + IntPtr.Size + (int)rgrdn); // walk IntPtr by CERT_RDN array element size: a dword and IntPtr } Marshal.FreeHGlobal(structinfo); } } //------ get friendly name for OID ------- private static string OIDName(String OID){ IntPtr oidinfo = Win32.CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, OID, 0) ; if(oidinfo == IntPtr.Zero) return null; IntPtr offsetoidname = (IntPtr)((int)oidinfo + 4 + IntPtr.Size); //3rd member of CRYPT_OID_INFO IntPtr pOIDname = Marshal.ReadIntPtr(offsetoidname, 0) ; string oidname = Marshal.PtrToStringUni(pOIDname); return oidname; } //----- Get RC2 keysize from encoded buffer (PKCS_RC2_CBC_PARAMETERS) ---------- private static int RC2Keysize(byte[] encodedRC2CBC) { IntPtr structinfo = IntPtr.Zero; int cbstruct = 0; int keysize = 0; if(Win32.CryptDecodeObject(MY_ENCODING_TYPE, PKCS_RC2_CBC_PARAMETERS, encodedRC2CBC, encodedRC2CBC.Length, 0, IntPtr.Zero, ref cbstruct)) { structinfo = Marshal.AllocHGlobal(cbstruct); Win32.CryptDecodeObject(MY_ENCODING_TYPE, PKCS_RC2_CBC_PARAMETERS, encodedRC2CBC, encodedRC2CBC.Length, 0, structinfo, ref cbstruct); CRYPT_RC2_CBC_PARAMETERS rc2cbcparms = (CRYPT_RC2_CBC_PARAMETERS) Marshal.PtrToStructure(structinfo, typeof(CRYPT_RC2_CBC_PARAMETERS)) ; int keysizecode = rc2cbcparms.dwVersion; Marshal.FreeHGlobal(structinfo); switch(keysizecode) { case CRYPT_RC2_40BIT_VERSION: keysize = 40; break; case CRYPT_RC2_56BIT_VERSION: keysize = 56; break; case CRYPT_RC2_64BIT_VERSION: keysize = 64; break; case CRYPT_RC2_128BIT_VERSION: keysize = 128; break; default: keysize = 0; break; } } return keysize; } 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 Usage() { Console.WriteLine("\nUsage:\nEnvelInfo "); } private static void showWin32Error(int errorcode){ Win32Exception myEx=new Win32Exception(errorcode); Console.WriteLine("Error message: {0} (Code: 0x{1:X})", myEx.Message, myEx.ErrorCode); } } }