internal static extern bool CryptEncodeObjectEx( [In] uint dwCertEncodingType, [In, MarshalAs(UnmanagedType.LPStr)] string lpszStructType, [In] ref CERT_ALT_NAME_INFO pvStructInfo, [In] uint dwFlags, [In, MarshalAs(UnmanagedType.SysInt)] IntPtr pEncodePara, [Out] byte[] pvEncoded, [In, Out] ref int pcbEncoded);
private static byte[] EncodeExtension(IList <X509AlternativeName> altNames) { var certAltName = new CERT_ALT_NAME_INFO(); certAltName.cAltEntry = (uint)altNames.Count; var structSize = Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)); var altNamesBuffer = Marshal.AllocHGlobal(structSize * altNames.Count); var unionValues = new List <IntPtr>(); try { for (int index = 0, offset = 0; index < altNames.Count; index++, offset += structSize) { var altName = new CERT_ALT_NAME_ENTRY(); altName.dwAltNameChoice = (CertAltNameChoice)altNames[index].Type; switch (altName.dwAltNameChoice) { case CertAltNameChoice.CERT_ALT_NAME_DNS_NAME: altName.Value = new CERT_ALT_NAME_ENTRY_UNION { pwszDNSName = Marshal.StringToHGlobalUni((string)altNames[index].Value) }; unionValues.Add(altName.Value.pwszDNSName); break; case CertAltNameChoice.CERT_ALT_NAME_URL: altName.Value = new CERT_ALT_NAME_ENTRY_UNION { pwszURL = Marshal.StringToHGlobalUni((string)altNames[index].Value) }; unionValues.Add(altName.Value.pwszURL); break; case CertAltNameChoice.CERT_ALT_NAME_IP_ADDRESS: var ip = (IPAddress)altNames[index].Value; var addressBytes = ip.GetAddressBytes(); var ipBytes = Marshal.AllocHGlobal(addressBytes.Length); Marshal.Copy(addressBytes, 0, ipBytes, addressBytes.Length); altName.Value = new CERT_ALT_NAME_ENTRY_UNION { IPAddress = new CRYPTOAPI_BLOB { cbData = (uint)addressBytes.Length, pbData = ipBytes } }; unionValues.Add(ipBytes); break; } Marshal.StructureToPtr(altName, IntPtrArithmetic.Add(altNamesBuffer, offset), false); } certAltName.rgAltEntry = altNamesBuffer; uint dataSize = 0; LocalBufferSafeHandle data; if (!Crypt32.CryptEncodeObjectEx(EncodingType.X509_ASN_ENCODING, OIDs.szOID_SUBJECT_ALT_NAME2, ref certAltName, 0x8000, IntPtr.Zero, out data, ref dataSize)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } using (data) { var buffer = new byte[dataSize]; Marshal.Copy(data.DangerousGetHandle(), buffer, 0, (int)dataSize); return(buffer); } } finally { Marshal.FreeHGlobal(altNamesBuffer); unionValues.ForEach(Marshal.FreeHGlobal); } }
private static byte[] EncodeExtension(IList<X509AlternativeName> altNames) { var certAltName = new CERT_ALT_NAME_INFO { cAltEntry = (uint)altNames.Count }; var structSize = Marshal.SizeOf<CERT_ALT_NAME_ENTRY>(); var altNamesBuffer = Marshal.AllocHGlobal(structSize * altNames.Count); var unionValues = new List<IntPtr>(); byte[] data = null; int dataSize = 0; try { for (int index = 0, offset = 0; index < altNames.Count; index++, offset += structSize) { var altName = new CERT_ALT_NAME_ENTRY { dwAltNameChoice = (uint)altNames[index].Type }; switch (altNames[index].Type) { case X509AlternateNameType.DnsName: altName.Value = new CERT_ALT_NAME_ENTRY_UNION { pwszDNSName = Marshal.StringToHGlobalUni((string)altNames[index].Value) }; unionValues.Add(altName.Value.pwszDNSName); break; case X509AlternateNameType.Url: altName.Value = new CERT_ALT_NAME_ENTRY_UNION { pwszURL = Marshal.StringToHGlobalUni((string)altNames[index].Value) }; unionValues.Add(altName.Value.pwszURL); break; case X509AlternateNameType.IPAddress: var ip = (IPAddress)altNames[index].Value; var addressBytes = ip.GetAddressBytes(); var ipBytes = Marshal.AllocHGlobal(addressBytes.Length); Marshal.Copy(addressBytes, 0, ipBytes, addressBytes.Length); altName.Value = new CERT_ALT_NAME_ENTRY_UNION { IPAddress = new CRYPTOAPI_BLOB { cbData = (uint)addressBytes.Length, pbData = ipBytes } }; unionValues.Add(ipBytes); break; } Marshal.StructureToPtr(altName, altNamesBuffer + offset, false); } certAltName.rgAltEntry = altNamesBuffer; if (!NativeMethods.CryptEncodeObjectEx( X509_ASN_ENCODING, OID_SUBJECT_ALT_NAME2, ref certAltName, 0, IntPtr.Zero, null, ref dataSize)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } data = new byte[dataSize]; if (!NativeMethods.CryptEncodeObjectEx( X509_ASN_ENCODING, OID_SUBJECT_ALT_NAME2, ref certAltName, 0, IntPtr.Zero, data, ref dataSize)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } return data; } finally { Marshal.FreeHGlobal(altNamesBuffer); unionValues.ForEach(Marshal.FreeHGlobal); } }
/// <summary> /// Parses an array of alternate names. /// </summary> private static void ParseAltNameInfo( CERT_ALT_NAME_INFO names, List<string> fields) { IntPtr pPos = names.rgAltEntry; for (int ii = 0; ii < names.cAltEntry; ii++) { CERT_ALT_NAME_ENTRY pEntry = (CERT_ALT_NAME_ENTRY)Marshal.PtrToStructure< CERT_ALT_NAME_ENTRY>(pPos); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf<CERT_ALT_NAME_ENTRY>()); switch (pEntry.dwAltNameChoice) { case CERT_ALT_NAME_URL: { string url = Marshal.PtrToStringUni(pEntry.Value.pwszURL); fields.Add("URL=" + url); break; } case CERT_ALT_NAME_DNS_NAME: { string dns = Marshal.PtrToStringUni(pEntry.Value.pwszURL); fields.Add("DNSName=" + dns); break; } case CERT_ALT_NAME_RFC822_NAME: { string email = Marshal.PtrToStringUni(pEntry.Value.pwszURL); fields.Add("Email=" + email); break; } case CERT_ALT_NAME_REGISTERED_ID: { string oid = Marshal.PtrToStringUni(pEntry.Value.pwszURL); fields.Add("OID=" + oid); break; } case CERT_ALT_NAME_IP_ADDRESS: { byte[] addressBytes = new byte[pEntry.Value.IPAddress.cbData]; Marshal.Copy(pEntry.Value.IPAddress.pbData, addressBytes, 0, addressBytes.Length); System.Net.IPAddress address = new System.Net.IPAddress(addressBytes); fields.Add("IPAddress=" + address.ToString()); break; } } } }
// creates the alternate name extension for the certificate. static void CreateSubjectAltNameExtension( string applicationUri, IList<string> hostNames, ref CERT_EXTENSION pExtension) { int count = hostNames.Count + 1; // initialize extension. pExtension.pszObjId = szOID_SUBJECT_ALT_NAME2; pExtension.fCritical = 0; IntPtr pData = IntPtr.Zero; int dwDataSize = 0; // build list of alternate names. IntPtr pAlternateNames = IntPtr.Zero; IntPtr pEntries = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY))*count); // create structure to encode. try { // set application uri. CERT_ALT_NAME_ENTRY pEntry = new CERT_ALT_NAME_ENTRY(); pEntry.dwAltNameChoice = CERT_ALT_NAME_URL; pEntry.Value.pwszURL = Marshal.StringToHGlobalUni(applicationUri); Marshal.StructureToPtr(pEntry, pEntries, false); IntPtr pPos = new IntPtr(pEntries.ToInt64() + Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY))); for (int ii = 0; ii < hostNames.Count; ii++) { System.Net.IPAddress ipAddress = null; // check for ip address. if (System.Net.IPAddress.TryParse(hostNames[ii], out ipAddress)) { byte[] bytes = ipAddress.GetAddressBytes(); pEntry.dwAltNameChoice = CERT_ALT_NAME_IP_ADDRESS; pEntry.Value.IPAddress.cbData = bytes.Length; pEntry.Value.IPAddress.pbData = AllocBytes(bytes); } // treat as DNS host name. else { pEntry.dwAltNameChoice = CERT_ALT_NAME_DNS_NAME; pEntry.Value.pwszDNSName = Marshal.StringToHGlobalUni(hostNames[ii]); } Marshal.StructureToPtr(pEntry, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY))); } CERT_ALT_NAME_INFO alternateNames = new CERT_ALT_NAME_INFO(); alternateNames.cAltEntry = count; alternateNames.rgAltEntry = pEntries; pAlternateNames = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_ALT_NAME_INFO))); Marshal.StructureToPtr(alternateNames, pAlternateNames, false); // calculate amount of memory required. int bResult = NativeMethods.CryptEncodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_SUBJECT_ALT_NAME2, // X509_ALTERNATE_NAME, pAlternateNames, 0, IntPtr.Zero, IntPtr.Zero, ref dwDataSize); if (bResult == 0) { throw new InvalidOperationException("Could not get size for subject alternate name extension."); } // allocate memory. pData = Marshal.AllocHGlobal(dwDataSize); // encode blob. bResult = NativeMethods.CryptEncodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_SUBJECT_ALT_NAME2, // X509_ALTERNATE_NAME, pAlternateNames, 0, IntPtr.Zero, pData, ref dwDataSize); if (bResult == 0) { throw new InvalidOperationException("Could not create subject alternate name extension."); } pExtension.Value.cbData = dwDataSize; pExtension.Value.pbData = pData; pData = IntPtr.Zero; } finally { if (pData != IntPtr.Zero) { Marshal.FreeHGlobal(pData); } if (pAlternateNames != IntPtr.Zero) { Marshal.DestroyStructure(pAlternateNames, typeof(CERT_ALT_NAME_INFO)); Marshal.FreeHGlobal(pAlternateNames); } if (pEntries != IntPtr.Zero) { IntPtr pPos = pEntries; for (int ii = 0; ii < count; ii++) { CERT_ALT_NAME_ENTRY pEntry = (CERT_ALT_NAME_ENTRY)Marshal.PtrToStructure(pPos, typeof(CERT_ALT_NAME_ENTRY)); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY))); switch (pEntry.dwAltNameChoice) { case CERT_ALT_NAME_URL: { Marshal.FreeHGlobal(pEntry.Value.pwszURL); break; } case CERT_ALT_NAME_DNS_NAME: { Marshal.FreeHGlobal(pEntry.Value.pwszDNSName); break; } case CERT_ALT_NAME_IP_ADDRESS: { Marshal.FreeHGlobal(pEntry.Value.IPAddress.pbData); break; } } } Marshal.FreeHGlobal(pEntries); } } }
private static byte[] EncodeExtension(IList <X509AlternativeName> altNames) { var certAltName = new CERT_ALT_NAME_INFO { cAltEntry = (uint)altNames.Count }; var structSize = Marshal.SizeOf <CERT_ALT_NAME_ENTRY>(); var altNamesBuffer = Marshal.AllocHGlobal(structSize * altNames.Count); var unionValues = new List <IntPtr>(); byte[] data = null; int dataSize = 0; try { for (int index = 0, offset = 0; index < altNames.Count; index++, offset += structSize) { var altName = new CERT_ALT_NAME_ENTRY { dwAltNameChoice = (uint)altNames[index].Type }; switch (altNames[index].Type) { case X509AlternateNameType.DnsName: altName.Value = new CERT_ALT_NAME_ENTRY_UNION { pwszDNSName = Marshal.StringToHGlobalUni((string)altNames[index].Value) }; unionValues.Add(altName.Value.pwszDNSName); break; case X509AlternateNameType.Url: altName.Value = new CERT_ALT_NAME_ENTRY_UNION { pwszURL = Marshal.StringToHGlobalUni((string)altNames[index].Value) }; unionValues.Add(altName.Value.pwszURL); break; case X509AlternateNameType.IPAddress: var ip = (IPAddress)altNames[index].Value; var addressBytes = ip.GetAddressBytes(); var ipBytes = Marshal.AllocHGlobal(addressBytes.Length); Marshal.Copy(addressBytes, 0, ipBytes, addressBytes.Length); altName.Value = new CERT_ALT_NAME_ENTRY_UNION { IPAddress = new CRYPTOAPI_BLOB { cbData = (uint)addressBytes.Length, pbData = ipBytes } }; unionValues.Add(ipBytes); break; } Marshal.StructureToPtr(altName, altNamesBuffer + offset, false); } certAltName.rgAltEntry = altNamesBuffer; if (!NativeMethods.CryptEncodeObjectEx( X509_ASN_ENCODING, OID_SUBJECT_ALT_NAME2, ref certAltName, 0, IntPtr.Zero, null, ref dataSize)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } data = new byte[dataSize]; if (!NativeMethods.CryptEncodeObjectEx( X509_ASN_ENCODING, OID_SUBJECT_ALT_NAME2, ref certAltName, 0, IntPtr.Zero, data, ref dataSize)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } return(data); } finally { Marshal.FreeHGlobal(altNamesBuffer); unionValues.ForEach(Marshal.FreeHGlobal); } }