Example #1
0
 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);
            }
        }