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/2010/12/20/how-to-call-cryptmsg-api-in-streaming-mode-largecms-full-sample/
Post name: How to call CryptMsg API in streaming mode – LargeCMS full sample
Original author: Alejandro Campos Magencio
Posting date: 2010-12-20T03:47:00+00:00


Hi all

Some time ago I posted a sample which shows how to sign large CMS data by calling CryptMsg API directly (How to call CryptMsg API in streaming mode (C#)) and that way overcome the current limitations of SignedCms in .NET ("ASN1 value too large" error when calling SignedCms.ComputeSignature).

I continued working on the sample and extended it quite a lot.Current version implementsSignedLargeCMS and EnvelopedLargeCMS, my own version ofSignedCms and EnvelopedCMS to work with large data.

I already shared thep/invoke declarationsI used (P/Invoking CryptoAPI in .NET. Part 2 (C#)), and you can findthe full version of the sample at Codeplex.

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/12/20/big-delay-while-calling-envelopedcms-constructor/
Post name: Big delay while calling EnvelopedCms constructor
Original author: Alejandro Campos Magencio
Posting date: 2010-12-20T02:53:00+00:00


Hi all,

 

You may experience a big delay when calling EnvelopedCms constructor in your .NET application if you have networking problems. For example, if DNS server is not available, a call to the constructor may take 30 to 90 seconds!!!

 

I could repro the issue by running the following code:

DateTime before = DateTime.Now;
EnvelopedCms envelopedCms = new EnvelopedCms();
MessageBox.Show("Duration = " + DateTime.Now.Subtract(before).TotalMilliseconds.ToString() + " milliseconds");

In my test environment for instance, if DNS is correctly set, the duration of the first call to the constructor of EnvelopedCms will take ~1 second, while subsequent calls to the constructor in the same instance of the process will take ~0 secs. Now, if I set the DNS to a dummy 1.1.1.1, the first call to the constructor will take ~85 secs, more or less the same as subsequent calls.

I debugged the issue, and saw that the delay is being caused by a couple calls toCryptFindOIDInfo API that the constructor makes behind the scenes to do an OID lookup.It calls CryptFindOIDInfo with certain parameters that will always cause the API to go to the network trying to reach Active Directory. This API is documented to behave that way:

CryptFindOIDInfo Function

"

The CryptFindOIDInfo function performs a lookup in the active directory to retrieve the friendly names of OIDs under the following conditions:

  • The key type in the dwKeyType parameter is set to CRYPT_OID_INFO_OID_KEY or CRYPT_OID_INFO_NAME_KEY.
  • No group identifier is specified in the dwGroupId parameter or the GroupID refers to EKU OIDs, policy OIDs or template OIDs.

Network retrieval of the friendly name can be suppressed by calling the function with the CRYPT_OID_DISABLE_SEARCH_DS_FLAG flag.

And the constructor won't useCRYPT_OID_DISABLE_SEARCH_DS_FLAG when calling the API.

Note that thissame issue applies to other .NET Security classes likeSignedCms orRSACryptoServiceProvider.The following article proposes a workaround for this issue when working with RSACryptoServiceProvider, but it won't apply to EnvelopedCms orSignedCmsscenarios:

Problem: Delay while calling RSACryptoServiceProvider SignData or VerifyData methods

In order to workaround the issue withEnvelopedCms, youjust need to run the following code once with an admin user. The code will register locally the OIDs that EnvelopedCms constructor is looking for in AD. That way it won’t need to go to the network for the info. You may create a tiny command line EXE that you can run before running your app:

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.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Win32;

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

        private void button1_Click(object sender, EventArgs e)
        {
            DateTime before = DateTime.Now;

            // Repro the issue
            EnvelopedCms envelopedCms = new EnvelopedCms();

            MessageBox.Show("Duration = " + DateTime.Now.Subtract(before).TotalMilliseconds.ToString() + " Milliseconds");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                // Register all the OIDs that EnvelopedCms constructor tries to use with CryptFindOIDInfo
                RegisterOID(szOID_RSA_data);
                RegisterOID(szOID_RSA_DES_EDE3_CBC);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }
        }

        public void RegisterOID(string oid)
        {
            // Get OID info
            IntPtr info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid, 0);

            if (info.Equals(IntPtr.Zero))
            {
                // Error
                throw new Exception("CryptFindOIDInfo didn't find OID \"" + oid + "\"");
            }

            // Register it
            bool success = CryptRegisterOIDInfo(info, CRYPT_INSTALL_OID_INFO_BEFORE_FLAG);
            if (!success)
            {
                // Error
                throw new Exception("CryptRegisterOIDInfo failed for OID \"" + oid + "\". Error #" + Marshal.GetLastWin32Error().ToString());
            }

            // Modify registry info (this is because .NET uses CRYPT_OID_INFO_NAME_KEY to look for the OID, 
            // instead of CRYPT_OID_INFO_OID_KEY, and in these cases it is looking by OID, and not by name)
            CRYPT_OID_INFO oidInfo = new CRYPT_OID_INFO();
            Marshal.PtrToStructure(info, oidInfo);

            bool isWow64 = false;
            IsWow64Process(Process.GetCurrentProcess().Handle, ref isWow64);

            string strRegKey = "";
            if (isWow64)
            {
                // We are running x86 app in x64 bit Windows
                strRegKey = "Software\\Wow6432Node\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\" + oidInfo.pszOID + "!" + oidInfo.dwGroupId.ToString();
            }
            else
            {
                // We are running x86 app in x86 Windows or x64 app in x64 Windows
                strRegKey = "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\" + oidInfo.pszOID + "!" + oidInfo.dwGroupId.ToString();
            }

            RegistryKey Key = null;
            Key = Registry.LocalMachine.CreateSubKey(strRegKey);
            Key.SetValue("Name", oidInfo.pszOID);
            Key.Close();
        }

        public const int CRYPT_OID_INFO_OID_KEY = 1;
        public const int CRYPT_OID_INFO_NAME_KEY = 2;
        public const string szOID_RSA_data = "1.2.840.113549.1.7.1";
        public const string szOID_RSA_DES_EDE3_CBC = "1.2.840.113549.3.7";
        public const int CRYPT_INSTALL_OID_INFO_BEFORE_FLAG = 1;

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPTOAPI_BLOB
        {
            public int cbData;
            public IntPtr pbData;
        } 

        [StructLayout(LayoutKind.Sequential)]
        public class CRYPT_OID_INFO
        {
            public int cbSize;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pszOID;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pwszName;
            public int dwGroupId;
            public int dwValueOrAlgidordwLength;
            public CRYPTOAPI_BLOB ExtraInfo;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pwszCNGAlgid;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pwszCNGExtraAlgid;
        } 

        [DllImport("crypt32.dll", SetLastError=true)]
        public static extern IntPtr CryptFindOIDInfo(
            int dwKeyType,
            string pvKey,
            uint dwGroupId
        );

        [DllImport("crypt32.dll", SetLastError = true)]
        public static extern bool CryptRegisterOIDInfo(
            IntPtr pInfo,
            int dwFlags
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool IsWow64Process(
            IntPtr hProcess,
            ref bool Wow64Process
        );         
    }
}

Note that this code won't work as is on Windows Server 2003 or Windows XP. The pwszCNGAlgid and pwszCNGExtraAlgid fields of CRYPT_OID_INFO are not available on OS versions older than Vista:

CRYPT_OID_INFO Structure

pwszCNGAlgid

Windows Server 2003, Windows XP, and Windows 2000: This member is not available.

 

pwszCNGExtraAlgid

Windows Server 2003, Windows XP, and Windows 2000: This member is not available.

So we should use this class instead:

        [StructLayout(LayoutKind.Sequential)]
        public class CRYPT_OID_INFO
        {
            public int cbSize;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pszOID;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pwszName;
            public int dwGroupId;
            public int dwValueOrAlgidordwLength;
            public CRYPTOAPI_BLOB ExtraInfo;
        }

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/12/15/how-to-enumerate-all-certificates-on-a-smart-card-powershell/
Post name: How to enumerate all certificates on a smart card (PowerShell)
Original author: Alejandro Campos Magencio
Posting date: 2010-12-15T00:57:00+00:00


Hi all,

Some time ago I assisted my colleague Jeff Bowles with the development of a PowerShell script which enumerates all certificates on a smart card.Basically the replacement to CAPICOM.Store.Open CAPICOM_SMART_CARD_USER_STORE.

He developed a sample that returns aSystem.Security.Cryptography.X509Certificates.X509Store object with the certificates in the card. The sample tries to emulatewhat logonUI.exe does during smart card logon as documented
atCertificate Enumeration.

Note that this code is also a great example that shows how we can use PowerShell to call Win32 API the same way we do it with any .NET application through P/Invoke mechanism:

function Get-SCUserStore {

[string]$providerName ="Microsoft Base Smart Card Crypto Provider"

# import CrytoAPI from advapi32.dll
$signature = @"
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetProvParam(
IntPtr hProv,
uint dwParam,
byte[] pbProvData,
ref uint pdwProvDataLen,
uint dwFlags);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDestroyKey(
IntPtr hKey);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string pszContainer,
string pszProvider,
uint dwProvType,
long dwFlags);

[DllImport("advapi32.dll", CharSet=CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetUserKey(
IntPtr hProv,
uint dwKeySpec,
ref IntPtr phUserKey);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetKeyParam(
IntPtr hKey,
uint dwParam,
byte[] pbData,
ref uint pdwDataLen,
uint dwFlags);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptReleaseContext(
IntPtr hProv,
uint dwFlags);
"@

$CryptoAPI = Add-Type -member $signature -name advapiUtils -Namespace CryptoAPI -passthru

# set some constants for CryptoAPI
$AT_KEYEXCHANGE = 1
$AT_SIGNATURE = 2
$PROV_RSA_FULL = 1
$KP_CERTIFICATE = 26
$PP_ENUMCONTAINERS = 2
$PP_CONTAINER = 6
$PP_USER_CERTSTORE = 42
$CRYPT_FIRST = 1
$CRYPT_NEXT = 2
$CRYPT_VERIFYCONTEXT = 0xF0000000

[System.IntPtr]$hProvParent=0
$contextRet = $CryptoAPI::CryptAcquireContext([ref]$hprovParent,$null,$providerName,$PROV_RSA_FULL,$CRYPT_VERIFYCONTEXT)

[Uint32]$pdwProvDataLen = 0
[byte[]]$pbProvData = $null
$GetProvParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_CONTAINER,$pbProvData,[ref]$pdwProvDataLen,0)

if($pdwProvDataLen -gt 0)
{
$ProvData = new-Object byte[] $pdwProvDataLen
$GetKeyParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_CONTAINER,$ProvData,[ref]$pdwProvDataLen,0)
}

$enc = new-object System.Text.UTF8Encoding($null)
$keyContainer = $enc.GetString($ProvData)

write-host " The Default User Key Container:" $keyContainer

[Uint32]$pdwProvDataLen = 0
[byte[]]$pbProvData = $null
$GetProvParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_USER_CERTSTORE,$pbProvData,[ref]$pdwProvDataLen,0)

if($pdwProvDataLen -gt 0)
{
$ProvData = new-Object byte[] $pdwProvDataLen
$GetKeyParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_USER_CERTSTORE,$ProvData,[ref]$pdwProvDataLen,0)
[uint32]$provdataInt = [System.BitConverter]::ToUInt32($provdata,0)
[System.IntPtr]$hwStore = $provdataInt
}

$store = new-object System.Security.Cryptography.X509Certificates.X509Store($hwStore)

# release smart card
$ReleaseContextRet = $CryptoAPI::CryptReleaseContext($hprovParent,0)

return $store
}

write-host ((get-WmiObject win32_PnPSignedDriver|where{$_.deviceID -like "*smartcard*"}).devicename) "reports the following certificates;"

# returns System.Security.Cryptography.X509Certificates.X509Store object representing PP_USER_CERTSTORE on Smart Card
$SCcertStore = Get-SCuserSTore

# enumerate certificates
$SCcertStore.certificates

I hope this helps. AndTHANKS A LOT JEFFfor sharing the final sample with me and the community!!! Kudos!!!

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/10/29/would-you-like-a-free-code-sample-from-microsoft/
Post name: Would you like a free code sample from Microsoft?
Original author: Alejandro Campos Magencio
Posting date: 2010-10-29T00:16:50+00:00


Hi all,

Many times I get requests through my blog to provide code samples on some specific topics. Unfortunately I don't have time to fulfill such requests. Usually the only way for me to be able to do that is if you open what we call an Advisory case with MS Technical Support, a service that is only available for our customers with a PREMIER account.

But check this out! Now there is another alternative to get code samples, for FREE!!!!

FREE Code Sample Request Service from Microsoft All-In-One Code Framework

"

Today, the Microsoft All-In-One Code Framework team announces a NEW, FREE code sample request service. This service is a proactive way for our developer community to obtain code samples for certain programming tasks directly from Microsoft. We want to alleviate the frustration felt by developers.

Developers are encouraged to submit code sample requests dealing with any Microsoft development technologies to our site. At the same time, developers can now vote for newly submitted or existing code sample topics. Here’s the exciting part! Microsoft engineers will then pick the requests with the highest number of votes and provide the code samples. It’s all FREE!

"

I hope you like this new service!

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/10/20/exportparameters-returns-invalid-type-specified-error/
Post name: ExportParameters returns Invalid type specified error
Original author: Alejandro Campos Magencio
Posting date: 2010-10-20T07:29:41+00:00


Hi all,

Some time ago a customer of mine was trying to export the private key associated to a certificate stored in a smart card, and for that he was trying to use theRSACryptoServiceProvider.ExportParameters method with a code like the following:

"

System.Security.Cryptography.X509Certificates.X509Certificate2 cert = GetCert(certName);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters params = rsa.ExportParameters(true);

"

But ExportParameters was returning the following exception:

"

System.Security.Cryptography.CryptographicException: Invalid type specified

"

To troubleshoot this error, we took traces with the following debugger script of mine:CryptoAPI Tracer script.

With those traces we saw that the error came from CryptExportKey API. The reason of the error was that the third-party Cryptographic Service Provider (CSP) associated to the certificate won’t allow us to export the private keys from the smart card. This actually makes sense if we think about the main purpose of smart cards in this scenario: hold private keys which can only be accessed directly from the smart card whenever they are needed to sign or decrypt data.

For completeness, this is the relevant sequence of calls written by the script:

"

>>>>>>>>>>>>>>>>>>>>>>

CryptAcquireContextW (0xc58)

IN

pszContainer

002817c0 "Some Container"

pszProvider

0612e828 "My third-party smart card Prov"

0612e868 "ider"

dwProvType

PROV_RSA_FULL

dwFlags

0

OUT

hProv

0x6bd87d8

RESULT

CryptAcquireContextW (0xc58) SUCCEEDED

<<<<<<<<<<<<<<<<<<<<<<

>>>>>>>>>>>>>>>>>>>>>>

CryptGetUserKey (0xc58)

IN

hProv

0x6bd87d8

dwKeySpec

AT_SIGNATURE

OUT

hUserKey

0x6b0d800

RESULT

CryptGetUserKey (0xc58) SUCCEEDED

<<<<<<<<<<<<<<<<<<<<<<

>>>>>>>>>>>>>>>>>>>>>>

CryptExportKey (0xc58)

IN

hKey

0x6b0d800

hExpKey

0

dwBlobType

PRIVATEKEYBLOB

dwFlags

0

pbData

NULL

dwDataLen

4

OUT

dwDataLen

4

RESULT

CryptExportKey (0xc58) FAILED

LastErrorValue: (HRESULT) 0x8009000a (2148073482) -Invalid type specified.

LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0

<<<<<<<<<<<<<<<<<<<<<<

"

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)