Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2011/09/27/schannel-does-not-support-ssl-fragmentation/
Post name: SChannel does not support SSL Fragmentation
Original author: Alejandro Campos Magencio
Posting date: 2011-09-27T02:55:27+00:00


Hi all,

When connecting to an SSL-enabled web site with Internet Explorer, the client and server must negotiate an SSL session. The server sends its list of trusted root certificates to the client in the form of a non-encrypted record. The server requires that the client have a digital certificate for authentication, the client is able to select one that corresponds to a root certificate trusted by the server.

The problem appears when the root certificate list sent by the server exceeds the 16k size limit. This is defined in RFC 2246 (http://www.ietf.org/rfc/rfc2246.txt). When the list exceeds the 16k limit, the server sends several packets. Now, Internet Explorer relies its security to the operating system via Schannel. Schannel on Windows XP / Server 2003and Windows Vista / Server 2008 is not able to process those packets by default. Sowhen Internet Explorer tries to connect to the SSL-enabled web site, the connection fails.

This issue won't happen on Windows 7 / Server 2008 R2, as they support record fragmentation in Schannel.They can manage the rest of the packets sent by the web server with the rest of the trusted root certificate list.

More information:

SSL/TLS Record Fragmentation Support

2215054 SChannel does not support SSL Fragmentation

2219505 Internet Explorer was failing to connect to a SSL/TLS enabled web site, Schannel issue

Fortunatelly we recently released an update to overcome this limitation for older versions of the OS:

An update that enables Internet Explorer in Windows XP, in Windows Vista, or in Windows Server 2008 to parse fragmented TLS/SSL handshake messages is available

Note this update should be available as a High Priority update in Windows Update, too.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2011/09/27/big-delay-when-calling-sslstream-authenticateasclient/
Post name: Big delay when calling SslStream.AuthenticateAsClient
Original author: Alejandro Campos Magencio
Posting date: 2011-09-27T02:34:00+00:00


Hi all,

Some time ago I had a customer who was opening a SSL connection on his C# application, and he was calling SslStream.AuthenticateAsClient. His problem was that this call was taking around 15 seconds for each connection he made.

I debugged the issue and saw that the time that SslStream.AuthenticateAsClient was taking went to the process of building the certificate chain of the SSL server certificate with X509Chain.Build. The Build method ended up calling CertGetCertificateChain API, which took all the time. Digging a bit further to understand why CertGetCertificateChain took so long, I saw that we were trying to download the following file from the Internet:

http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/authrootstl.cab

Why were we downloading this file? Well, this will happen by default on Windows when we build the chain of a cert which root CA cert is not installed in the system. This is called the Automatic Root Certificates Update feature, and it is available on Windows XP/Server 2003 and later OS versions, including Windows 7/Server 2008 R2.

Here you can find more info on this feature and why we are using it in this scenario:

Certificate Support and Resulting Internet Communication in Windows 7 and Windows Server 2008 R2
"
How Update Root Certificates communicates with Internet sites


This subsection focuses on how the Update Root Certificates feature communicates with Internet sites.

If the Update Root Certificates feature has not been turned off through Group Policy, and the application on your server is presented with a certificate issued by a root CA that is not directly trusted, the Update Root Certificates feature communicates across the Internet as follows:
Specific information sent or received: The Update Root Certificates feature sends a request to the Windows Update Web site, asking for the current list of root CAs in the Microsoft Root Certificate Program. If the root CA that is not directly trusted is on the list, Update Root Certificates obtains the certificate for that root CA and places it in the trusted certificate store on the server. No authentication or unique identification of the administrator or user is used in this exchange.


Default setting and ability to disable: Update Root Certificates is turned on by default in Windows 7 and Windows Server 2008 R2. You can turn off this feature by using Group Policy. For more information, see Procedures for viewing or changing Group Policy settings that affect certificates in Windows 7 and Windows Server 2008 R2 later in this section.


Trigger and user notification: Update Root Certificates is triggered when the administrator or user at the computer is presented with a certificate issued by a root CA that is not directly trusted. There is no user notification.


Logging: Events are logged in Event Viewer. To locate the events, click Windows Logs, click Application, and the Source is CAPI2. Events containing information such as the following are logged:

For Event ID 4100:

Description: Successful auto update retrieval of a non-Microsoft root list sequence number from: URL_for_Windows_Update_Web_Site

For Event ID 4101:

Description: Failed auto update retrieval of a non-Microsoft root list sequence number from: URL_for_Windows_Update_Web_Site with error: hexadecimal_error_value


Encryption, privacy, and storage: When requests or certificates are sent to or from Update Root Certificates, no encryption is used. Microsoft does not track access to the list of trusted CAs that it maintains on the Windows Update Web site.


Transmission protocol and port: The transmission protocol is HTTP and the port is 80.
"

For older versions of the OS:

Certificate Support and Resulting Internet Communication in Windows Vista

Specific information sent or received: The Update Root Certificates feature sends a request to http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en, asking for the current list of root certification authorities in the Microsoft Root Certificate Program. If the root CA that is not directly trusted is named in the list, Update Root Certificates obtains the certificate for that root CA and places it in the trusted certificate store on the user's computer. No user authentication or unique user identification is used in this exchange.

Certificate Support and the Update Root Certificates Component
http://technet.microsoft.com/en-us/library/bb457160.aspx


Summing up, this behavior is by design. Options we have are:
1) Install the root CA cert locally so we don’t need to go to the Internet for the list of trusted root CA certs.
2) Disable the Automatic Root Certificates Update feature via GPO so we don’t go to the Internet in any case.

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2011/09/26/how-to-create-a-memory-mapped-file-with-c-windows-vista-windows-7/
Post name: How to create a memory mapped file with C# (Windows Vista / Windows 7)
Original author: Alejandro Campos Magencio
Posting date: 2011-09-26T07:14:00+00:00


Hi all,

The following C# sample shows how to create a memory mapped file and use private namespaces to allow access to specific groups of users:

FORM1.CS

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.IO;

namespace Alejacma
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
bool bResult = false;
IntPtr hBoundary = IntPtr.Zero;
IntPtr pSid = IntPtr.Zero;
int cbSid = Win32.SECURITY_MAX_SID_SIZE;
IntPtr hNamespace = IntPtr.Zero;
Win32.SECURITY_ATTRIBUTES securityAttributes = new Win32.SECURITY_ATTRIBUTES();
IntPtr hFile = IntPtr.Zero;
IntPtr pView = IntPtr.Zero;
IntPtr pData = IntPtr.Zero;

try
{
// Create boundary
hBoundary = Win32.CreateBoundaryDescriptor(
"AlejacmaBoundaryDescriptor",
0
);
if (hBoundary == IntPtr.Zero) { throw new Exception("CreateBoundaryDescriptor", new Win32Exception(Marshal.GetLastWin32Error())); }

pSid = Marshal.AllocHGlobal(cbSid);
bResult = Win32.CreateWellKnownSid(
WellKnownSidType.BuiltinAdministratorsSid,
IntPtr.Zero,
pSid,
ref cbSid
);
if (!bResult) { throw new Exception("CreateWellKnownSid", new Win32Exception(Marshal.GetLastWin32Error())); }

bResult = Win32.AddSIDToBoundaryDescriptor(
ref hBoundary,
pSid
);
if (!bResult) { throw new Exception("AddSIDToBoundaryDescriptor", new Win32Exception(Marshal.GetLastWin32Error())); }

// Create namespace and give access to some groups of users:
// - Remote Desktop Users (needed for remote users to access the mapped file)
// - Administrators (needed to create the mapped file within this program)
// - Interactive users (needed for local users to access mapped file)
bResult = Win32.ConvertStringSecurityDescriptorToSecurityDescriptor(
"D:(A;;GA;;;RD)(A;;GA;;;S-1-5-32-544)(A;;GA;;;S-1-5-4)",
Win32.SDDL_REVISION_1,
out securityAttributes.lpSecurityDescriptor,
IntPtr.Zero
);
if (!bResult) { throw new Exception("ConvertStringSecurityDescriptorToSecurityDescriptor", new Win32Exception(Marshal.GetLastWin32Error())); }

securityAttributes.nLength = Marshal.SizeOf(securityAttributes);
securityAttributes.bInheritHandle = false;
hNamespace = Win32.CreatePrivateNamespace(
ref securityAttributes,
hBoundary,
"Alejacma"
);
if (hNamespace == IntPtr.Zero) { throw new Exception("CreatePrivateNamespace", new Win32Exception(Marshal.GetLastWin32Error())); }

// Create file mapping, and give access to all users with access to the namespace
hFile = Win32.CreateFileMapping(
Win32.INVALID_HANDLE_VALUE,
ref securityAttributes,
Win32.PAGE_READWRITE,
0,
20971520,
"Alejacma\\TestFileMapping"
);
if (hFile == IntPtr.Zero) { throw new Exception("CreateFileMapping", new Win32Exception(Marshal.GetLastWin32Error())); }

// Map file and write something to it
pView = Win32.MapViewOfFile(
hFile,
Win32.FILE_MAP_WRITE,
0,
0,
11
);
if (pView == IntPtr.Zero) { throw new Exception("MapViewOfFile", new Win32Exception(Marshal.GetLastWin32Error())); }

pData = Marshal.StringToHGlobalAnsi("Hello World");
Win32.MemCopy(pView, pData, 11);

// We are done. Now a reader app can access the data
MessageBox.Show("Run the reader app now!");
}
catch (Exception ex)
{
// Any error?
MessageBox.Show(ex.Message + " failed with '" + ex.InnerException.Message + "' error");
}
finally
{
// Clean up memory
if (pSid != IntPtr.Zero)
{
Marshal.FreeHGlobal(pSid);
}

if (securityAttributes.lpSecurityDescriptor != IntPtr.Zero)
{
Win32.LocalFree(securityAttributes.lpSecurityDescriptor);
}

if (pData != IntPtr.Zero)
{
Marshal.FreeHGlobal(pData);
}

// We must also close hBoundary, hNamespace, hFile and pView when we don't need them anymore.
// Check MSDN's description of the APIs we used to create them to figure out how to close them.
}
}
}
}

WIN32.CS

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Alejacma
{
class Win32
{
public const int SECURITY_MAX_SID_SIZE = 68;
public const int SDDL_REVISION_1 = 1;
public const uint INVALID_HANDLE_VALUE = 0xffffffff;
public const int PAGE_READWRITE = 0x04;
public const int FILE_MAP_WRITE = 0X02;

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateBoundaryDescriptor
(
[In] string Name,
[In] int Flags
);

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CreateWellKnownSid
(
[In] WellKnownSidType WellKnownSidType,
[In] [Optional] IntPtr DomainSid,
[In] IntPtr pSid,
[In][Out]ref int cbSid
);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AddSIDToBoundaryDescriptor
(
[In][Out] ref IntPtr BoundaryDescriptor,
[In] IntPtr RequiredSid
);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor
(
[In] string StringSecurityDescriptor,
[In] int StringSDRevision,
[Out] out IntPtr SecurityDescriptor,
[Out] IntPtr SecurityDescriptorSize
);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalFree([In] IntPtr hMem);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreatePrivateNamespace(
[In][Optional] ref SECURITY_ATTRIBUTES lpPrivateNamespaceAttributes,
[In] IntPtr lpBoundaryDescriptor,
[In] string lpAliasPrefix
);

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateFileMapping(
[In] uint hFile,
[In][Optional] ref SECURITY_ATTRIBUTES lpAttributes,
[In] int flProtect,
[In] int dwMaximumSizeHigh,
[In] int dwMaximumSizeLow,
[In][Optional] string lpName
);

[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
[In] IntPtr hFileMappingObject,
[In] int dwDesiredAccess,
[In] int dwFileOffsetHigh,
[In] int dwFileOffsetLow,
[In] int dwNumberOfBytesToMap
);

[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemCopy(IntPtr dest, IntPtr src, uint count);
}
}

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2011/06/21/how-to-verify-validity-of-certificates-with-net/
Post name: How to verify validity of certificates with .NET
Original author: Alejandro Campos Magencio
Posting date: 2011-06-21T06:42:58+00:00


Hi all,

The other day a customer of mine was trying to verify the validity of a certificate with a .NET code like the following:

Dim cert As X509Certificate2 = New X509Certificate2(filename) 

Dim chain As New X509Chain()
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online

chain.ChainPolicy.VerificationFlags = _
X509VerificationFlags.IgnoreCtlSignerRevocationUnknown Or _
X509VerificationFlags.IgnoreRootRevocationUnknown Or _
X509VerificationFlags.IgnoreEndRevocationUnknown Or _
X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown Or _
X509VerificationFlags.IgnoreCtlNotTimeValid

chain.Build(cert)
Dim bVerif As Boolean = cert.Verify()

He was wondering what was going to the value of bVerif, and if the following collection was going to be filled in case of bVerif being false:

Dim status As X509ChainStatusFlags
Dim info As String
Dim chainElement As X509ChainElement
For Each chainElement In chain.ChainElements
Dim chainStatus As X509ChainStatus
For Each chainStatus In chainElement.ChainElementStatus
status = chainStatus.Status
info = chainStatus.StatusInformation
MsgBox(info)
Next
Next

First of all, we don’t need to call “cert.Verify()” in the code above at all. If we want basic validation, we use Verify method, but if we want to control the validation with specific flags the way the code above is doing it, we have to use X509Chain and the Build method.

X509Certificate2.Verify Method
"
Performs a X.509 chain validation using basic validation policy.

Remarks
--------------------------------------------------------------------------------
This method builds a simple chain for the certificate and applies the base policy to that chain. If you need more information about a failure, validate the certificate directly using the X509Chain object.
"

Additionally, note that the Build method already returns if the certificate is valid or not with the flags we passed.

X509Chain.Build Method
"
Return Value
Type: System.Boolean
true if the X.509 certificate is valid; otherwise, false.
"

So something like this would do:"Dim bVerif As Boolean = chain.Build(cert)"

After we call Build method, X509ChainStatus should be filled with the errors if any. This sample from an Spanish blog shows how to get that info:

Validar certificados
"

objChain.Build(objCert); 
if (objChain.ChainStatus.Length != 0)
{
foreach (X509ChainStatus objChainStatus in objChain.ChainStatus)
Debug.Print(objChainStatus.Status.ToString() + " - " + objChainStatus.StatusInformation);
}

"

This is basically what happens when we call Build:

1)ChainStatus gets populated with all errors we find in the chain, regardless of the flags we pass to ChainPolicy.VerificationFlags. So if there is any error, ChainStatus.Length will always be > 0.
2)Build returns TRUE if ChainStatus.Length is 0 (no errors), or if ChainStatus.Length > 0 but we decided to ignore all those errors with some flags in ChainPolicy.VerificationFlags.

So basically, we should always check the value that Build returns, TRUE or FALSE, and if it is FALSE, then check the ChainStatus for the specific errors.

For example, let's take a certificate that is not time valid. With the following code, the call to Verify will return false, the first call to Build will return true (as there are errors in ChainStatus but we ignored them with VerificationFlags), and the second call to Build will return false (as there are errors and we didn't ignore them thx to X509VerificationFlags.NoFlag):

Imports System.IO
Imports System.Security.Cryptography.X509Certificates

Module Module1

Sub Main()
Dim s() As String = System.Environment.GetCommandLineArgs()
If s.Length < 2 Then
MsgBox("Usage : VbConsoleApp fileEncrypted, where fileEncrypted is the file that contains the dates encrypted" & vbCrLf)
Exit Sub
End If

Dim cert As X509Certificate2 = New X509Certificate2(s(1))
Console.WriteLine(cert.SubjectName.Name)

Dim bRet As Boolean = cert.Verify()
Console.WriteLine("Verify returns " + bRet.ToString())
Console.WriteLine("****************************************************")

Dim ch1 As New X509Chain()
ch1.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain
ch1.ChainPolicy.RevocationMode = X509RevocationMode.Online
ch1.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreCtlSignerRevocationUnknown Or _
X509VerificationFlags.IgnoreRootRevocationUnknown Or _
X509VerificationFlags.IgnoreEndRevocationUnknown Or _
X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown Or _
X509VerificationFlags.IgnoreCtlNotTimeValid Or _
X509VerificationFlags.AllowUnknownCertificateAuthority

Dim bRet1 As Boolean = ch1.Build(cert)
Console.WriteLine("Build with flags returns " + bRet1.ToString())

For Each status1 As X509ChainStatus In ch1.ChainStatus
Console.WriteLine(status1.Status.ToString() + " --> " + status1.StatusInformation)
Next
Console.WriteLine("****************************************************")

Dim ch2 As New X509Chain()
ch2.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain
ch2.ChainPolicy.RevocationMode = X509RevocationMode.Online
ch2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag

Dim bRet2 As Boolean = ch2.Build(cert)
Console.WriteLine("Build without flags returns " + bRet2.ToString())
For Each status2 As X509ChainStatus In ch2.ChainStatus
Console.WriteLine(status2.Status.ToString() + " --> " + status2.StatusInformation)
Next
Console.WriteLine("****************************************************")

Console.ReadKey()
End Sub

End Module

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2011/05/19/x509certificate2-raises-the-smart-card-resource-manager-is-not-running-exception/
Post name: X509Certificate2 raises "The Smart card resource manager is not running" exception
Original author: Alejandro Campos Magencio
Posting date: 2011-05-19T08:24:29+00:00


Hi all,

Some time ago a customer of mine was getting a CryptographicException with message "The Smart card resource manager is not running" when using X509Certificate2 object in a Windows service. This was the call stack at the point of exception:

at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)

at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)

at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()

at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)

at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()

The service was running as NETWORK SERVICE, and it worked fine when running as SYSTEM. Error happened on Windows 7, but everything worked fine on Windows XP.

The cause of the issue is that .NET is calling SCardEstablishContext API behind the scenes, and the user running the service doesn't have enough permissions to run the API. More details on the issue and potential solutions can be found in this other post of mine: SCardEstablishContext fails with SCARD_E_NO_SERVICE error.

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)