Archive

Posts Tagged ‘Win32’

Win32 libjpeg Source Manager

December 6th, 2012 No comments

I was recently running into problems using libjpeg-turbo together with the stdio FILE* API. So instead of using _wfopen_s, _tfopen_s etc. I decided to write my own source manager which does not rely on stdlib functionality and uses the Win32 CreateFile API instead.

First, we need to extend a jpeg_src_mgr structure to hold additional data.

#define JPEG_WIN32_SRC_MGR_BUFFER_SIZE ( 1 << 10 )
 
typedef struct _jpeg_win32_src_mgr
{
    jpeg_source_mgr mgr;
    unsigned int bytes_read;
    HANDLE file;
    unsigned char buffer[ JPEG_WIN32_SRC_MGR_BUFFER_SIZE ];
} jpeg_win32_src_mgr;

Then we define several callback functions which are used by the libjpeg API to retrieve data from our data source.

The win32_init_source() callback is a no-op since we perform initialization before handing over the source manager to the library.

static void win32_init_source( j_decompress_ptr cinfo ) 
{
}

Function win32_fill_input_buffer() is called when the library has consumed the entire data in the buffer. The return value should always be TRUE if no I/O suspension is needed. There is one peculiarity when handling truncated image files. To prevent the library to request further data on EOF, the recommendation is to insert an EOI marker to signal end of file.

static boolean win32_fill_input_buffer( j_decompress_ptr cinfo )
{
    jpeg_win32_src_mgr* src = (jpeg_win32_src_mgr*)cinfo->src;
    DWORD bytes_read = 0;
    if ( TRUE != ReadFile( src->file, src->buffer, JPEG_WIN32_SRC_MGR_BUFFER_SIZE, &bytes_read, NULL ) )
        ERREXIT( cinfo, JERR_FILE_READ );
    src->mgr.next_input_byte = src->buffer;
    src->mgr.bytes_in_buffer = (size_t)bytes_read;
    if ( 0 == src->mgr.bytes_in_buffer )
    {
        /* The image file is truncated. We insert EOI marker to tell the library to stop processing. */
        src->buffer[ 0 ] = (JOCTET)0xFF;
        src->buffer[ 1 ] = (JOCTET)JPEG_EOI;
        src->mgr.bytes_in_buffer = 2;
    }
    return TRUE;
}

The win32_skip_input_data() callback is called when the library wants to skip over some unnecessary data such as APP markers.
It is valid for the function to leave 0 bytes in the input buffer, in which case win32_fill_input_buffer() will be called.

static void win32_skip_input_data( j_decompress_ptr cinfo, long num_bytes )
{
    jpeg_win32_src_mgr* src = (jpeg_win32_src_mgr*)cinfo->src;
 
    if ( 1 > num_bytes )
        return;
    if ( num_bytes <= src->mgr.bytes_in_buffer )
    {
        src->mgr.next_input_byte += (size_t)num_bytes;
        src->mgr.bytes_in_buffer -= (size_t)num_bytes;
    }
    else
    {
        LARGE_INTEGER offset;
        offset.QuadPart = num_bytes - (long)src->mgr.bytes_in_buffer;
        src->mgr.bytes_in_buffer = 0;
        DWORD new_pos = SetFilePointer( src->file, offset.LowPart, &offset.HighPart, FILE_CURRENT );
        if ( INVALID_SET_FILE_POINTER == new_pos )
            ERREXIT( cinfo, JERR_FILE_READ );
    }
}

The win32_term_source() callback ensures there are no open handles left when the library is done processing.

static void win32_term_source( j_decompress_ptr cinfo ) 
{
    jpeg_win32_src_mgr* src = (jpeg_win32_src_mgr*)cinfo->src;
    if ( INVALID_HANDLE_VALUE != src->file  && NULL != src->file )
    {
        CloseHandle( src->file );
        src->file = NULL;
    }
}

Our new source manager is initialized via a call to jpeg_win32_src(), which will open the file and provide references to the callbacks defined above.

static void jpeg_win32_src( j_decompress_ptr cinfo, jpeg_win32_src_mgr * src, PCTSTR filename )
{
    src->file = CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
    if ( INVALID_HANDLE_VALUE == src->file )
        ERREXIT( cinfo, JERR_FILE_READ );
    src->bytes_read = 0;
    src->mgr.init_source = win32_init_source;
    src->mgr.fill_input_buffer = win32_fill_input_buffer;
    src->mgr.skip_input_data = win32_skip_input_data;
    src->mgr.resync_to_restart = jpeg_resync_to_restart;
    src->mgr.term_source = win32_term_source;
    src->mgr.bytes_in_buffer = 0;
    src->mgr.next_input_byte = (JOCTET*)src->buffer;
    cinfo->src = (jpeg_source_mgr*)src;
}

Usage of the new Win32 source manager is simple:

jpeg_decompress_struct cinfo;
jpeg_win32_src_mgr src_data;
 
jpeg_create_decompress( &cinfo );
jpeg_win32_src( &cinfo, &src_data, szFilename );
jpeg_read_header( &cinfo, TRUE ); 
/* ... */

It would be quite easy to modify the source manager code listed above to support reading from other Win32 sources such as named pipes, console buffer or I/O devices.

The entire file can be downloaded here: jpeg_win32_src.h

.NET RSACryptoServiceProvider PEM + DER Support

October 4th, 2012 7 comments

In .NET, RSACryptoServiceProvider greatly simplifies common tasks associated with public/private keys, such as signing of data and verifying a signature.
Unfortunately, there is only a single format (proprietary XML) available for importing and exporting public/private key data.
The two widely spread formats for key exchange, PEM and DER are not supported, which limits the usability of the class when working with different kinds of public key APIs.

There are a few workarounds though:

  • Write your own .NET code to support the desired formats, which might not be that simple, as the following blog post suggests.
  • Use a third party component, such as the PEMReader class of the Bouncy Castle Library (Java/C# port).
  • Leverage Microsoft Crypto API (CAPI), which provides support for opening and converting a variety of different public/private key file formats. Here I would like to demonstrate how this can be achieved by means of simple extension methods to the RSACryptoServiceProvider class.

Here is a sample RSA 1024 bit private key in PEM format:

-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDwIqfvxEjqHu8048x4wJ5EId6ASAbWdH5fzgHxvew5kXqECMNc
XzRqDVnDVPQT41UeZs8HxouBE+ZA8DfnVlHwP4EIeigOUaqy0sseKpO71tupFU+2
LjpcF6O7cVuLjt6476iYfSyrssK4hnmzVYGZNz16OSR9z/SuTd8BhohG4QIDAQAB
AoGBAOmEmhEUrN9XU8D4IVfv4DhbQ1c2M8gKovYhjEx8J6LX8O9C4lAKmRrkfrzv
+Sb59EVLLtrd3b2ZD1lpAMQrciMwC5PAa8da/J++lR1VjM5GbzqKjGtfx3WQlzNE
1ZaZ2FSY8lAPMM4uLczyD79PJQBsGCcx3KDJRR5ENp6an5cRAkEA/m1FEqol/KKh
xOyGsK4GVuansBXhrAgpwMlYLT+vF0gy1jzYQDNNQXzeQFYH6gZY66RTYFl3JPNL
8KXLyhwDLQJBAPGew6xkLBoYi4IO9I+NP/gIHzSiQeEl2OxZsgZiz0Yh5E9ndwMr
87jTX/4ZBwNlDC0E+MXsJpMSvTFNpw4rcwUCQQC5FU5JLKOjq79YnOPChWYxM2vL
Ka/YULvm9dGCYTCDFE9/EBYUZf2OZULctHjfYqyvBwRsM8j7hU26CzI7nbMlAkAA
kVjwXMPlw80AHzzf4XsXAB3ip8bz2nzqAUPz0+OczJOWxC15am8GLij5leF4VpJy
wKI9BNMKYW7kYMRVujBpAkEA7gQ8MGqjjrCAfOzrrC9ZuVdGRfEjUEdHMqiF+js7
XNBvnT5lBznUOd+eta6CGo7S5hjU7D3CEzmVGQfxUsRZ1w==
-----END RSA PRIVATE KEY-----

This is the corresponding public key in textual PEM representation:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIqfvxEjqHu8048x4wJ5EId6A
SAbWdH5fzgHxvew5kXqECMNcXzRqDVnDVPQT41UeZs8HxouBE+ZA8DfnVlHwP4EI
eigOUaqy0sseKpO71tupFU+2LjpcF6O7cVuLjt6476iYfSyrssK4hnmzVYGZNz16
OSR9z/SuTd8BhohG4QIDAQAB
-----END PUBLIC KEY-----

We cannot load these keys into the RSACryptoServiceProvider directly. However the class supports an import method which is compatible with CryptoAPI: ImportCspBlob().
With CAPI in return, we can do all the heavy lifting including format conversion which the .NET Framework does not support.
For public keys, the conversion process for PEM (string) key data requires the following steps:

  1. Converting a public key in PEM string format into DER representation.
    CryptStringToBinaryA( sPEM, (UInt32)sPEM.Length, CRYPT_STRING_FLAGS.CRYPT_STRING_BASE64HEADER, IntPtr.Zero, ref dwBinarySize, out dwSkip, out dwFlags ) )
  2. Retrieve the key through a CERT_PUBLIC_KEY_INFO struct.
    CryptDecodeObjectEx( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.X509_PUBLIC_KEY_INFO ), DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.CRYPT_DECODE_ALLOC_FLAG, IntPtr.Zero, ref pCertPublicKeyInfo, out dwCertPublicKeyInfoSize )
  3. Convert the returned RSA key into a Diffie-Hellman Version 3 Public Key BLOBs or DSS Version 3 Public Key BLOBs struct:
    CryptDecodeObjectEx( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.RSA_CSP_PUBLICKEYBLOB ), RSAData, (UInt32)RSAData.Length, CRYPT_DECODE_FLAGS.CRYPT_DECODE_ALLOC_FLAG, IntPtr.Zero, ref pCertPublicKeyBlob, out dwCertPublicKeyBlobSize )
  4. Call RSACryptoServiceProvider.ImportCspBlob() for the resulting binary data.

For private keys, the following steps are required:

  1. Converting a private key in PEM string format into DER representation.
    CryptStringToBinaryA( sPEM, (UInt32)sPEM.Length, CRYPT_STRING_FLAGS.CRYPT_STRING_BASE64HEADER, IntPtr.Zero, ref dwBinarySize, out dwSkip, out dwFlags ) )
  2. For private keys, retrieve a pointer to an RSA private key BLOB directly:
    CryptDecodeObjectEx( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.PKCS_RSA_PRIVATE_KEY ), DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.CRYPT_DECODE_ALLOC_FLAG, IntPtr.Zero, ref pRSAPrivateKeyBlob, out pRSAPrivateKeyBlobSize )
  3. Call RSACryptoServiceProvider.ImportCspBlob() for the resulting binary data.

If you wrap the above logic into extension methods for RSACryptoServiceProvider, importing keys in PEM format and signing data can be done with a few lines of code:

// -----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----
string sPrivateKeyPEM = File.ReadAllText( "PrivateKey.pem" );
 
using ( RSACryptoServiceProvider rsa = new RSACryptoServiceProvider() )
{
    rsa.PersistKeyInCsp = false;
    rsa.LoadPrivateKeyPEM( sPrivateKeyPEM );
    using ( SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider() )
        signature = rsa.SignData( dataToSign, sha1 );
}

Verifying the same signature also becomes easy:

// -----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----
string sPublicKeyPEM = File.ReadAllText( "PublicKey.pem" );
 
using ( RSACryptoServiceProvider rsa = new RSACryptoServiceProvider() )
{
    rsa.PersistKeyInCsp = false;
    rsa.LoadPublicKeyPEM( sPublicKeyPEM );
    using ( SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider() )
        bVerifyResultOriginal = rsa.VerifyData( dataToSign, sha1, signature );
}

Here is a cut-and-paste solution based on an extension class to RSACryptoServiceProvider:

/*********************************************************************************
 * Copyright (c) 2013, Christian Etter info at christian-etter dot de
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
 
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
 
/// <summary>Extension method for initializing a RSACryptoServiceProvider from PEM data string.</summary>
public static class RSACryptoServiceProviderExtension
{
    #region Methods
 
    /// <summary>Extension method which initializes an RSACryptoServiceProvider from a DER public key blob.</summary>
    public static void LoadPublicKeyDER( this RSACryptoServiceProvider provider, byte[] DERData )
    {
        byte[] RSAData = RSACryptoServiceProviderExtension.GetRSAFromDER( DERData );
        byte[] publicKeyBlob = RSACryptoServiceProviderExtension.GetPublicKeyBlobFromRSA( RSAData );
        provider.ImportCspBlob( publicKeyBlob );
    }
 
    /// <summary>Extension method which initializes an RSACryptoServiceProvider from a DER private key blob.</summary>
    public static void LoadPrivateKeyDER( this RSACryptoServiceProvider provider, byte[] DERData )
    {
        byte[] privateKeyBlob = RSACryptoServiceProviderExtension.GetPrivateKeyDER( DERData );
        provider.ImportCspBlob( privateKeyBlob );
    }
 
    /// <summary>Extension method which initializes an RSACryptoServiceProvider from a PEM public key string.</summary>
    public static void LoadPublicKeyPEM( this RSACryptoServiceProvider provider, string sPEM )
    {
        byte[] DERData = RSACryptoServiceProviderExtension.GetDERFromPEM( sPEM );
        RSACryptoServiceProviderExtension.LoadPublicKeyDER( provider, DERData );
    }
 
    /// <summary>Extension method which initializes an RSACryptoServiceProvider from a PEM private key string.</summary>
    public static void LoadPrivateKeyPEM( this RSACryptoServiceProvider provider, string sPEM )
    {
        byte[] DERData = RSACryptoServiceProviderExtension.GetDERFromPEM( sPEM );
        RSACryptoServiceProviderExtension.LoadPrivateKeyDER( provider, DERData );
    }
 
    /// <summary>Returns a public key blob from an RSA public key.</summary>
    internal static byte[] GetPublicKeyBlobFromRSA( byte[] RSAData )
    {
        byte[] data = null;
        UInt32 dwCertPublicKeyBlobSize = 0;
        if ( RSACryptoServiceProviderExtension.CryptDecodeObject( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING,
            new IntPtr( (int)CRYPT_OUTPUT_TYPES.RSA_CSP_PUBLICKEYBLOB ), RSAData, (UInt32)RSAData.Length, CRYPT_DECODE_FLAGS.NONE,
            data, ref dwCertPublicKeyBlobSize ) )
        {
            data = new byte[ dwCertPublicKeyBlobSize ];
            if ( !RSACryptoServiceProviderExtension.CryptDecodeObject( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING,
                new IntPtr( (int)CRYPT_OUTPUT_TYPES.RSA_CSP_PUBLICKEYBLOB ), RSAData, (UInt32)RSAData.Length, CRYPT_DECODE_FLAGS.NONE,
                data, ref dwCertPublicKeyBlobSize ) )
                throw new Win32Exception( Marshal.GetLastWin32Error() );
        }
        else
            throw new Win32Exception( Marshal.GetLastWin32Error() );
        return data;
    }
 
    /// <summary>Converts DER binary format to a CAPI CRYPT_PRIVATE_KEY_INFO structure.</summary>
    internal static byte[] GetPrivateKeyDER( byte[] DERData )
    {
        byte[] data = null;
        UInt32 dwRSAPrivateKeyBlobSize = 0;
        IntPtr pRSAPrivateKeyBlob = IntPtr.Zero;
        if ( RSACryptoServiceProviderExtension.CryptDecodeObject( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.PKCS_RSA_PRIVATE_KEY ),
            DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwRSAPrivateKeyBlobSize ) )
        {
            data = new byte[ dwRSAPrivateKeyBlobSize ];
            if ( !RSACryptoServiceProviderExtension.CryptDecodeObject( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.PKCS_RSA_PRIVATE_KEY ),
                DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwRSAPrivateKeyBlobSize ) )
                throw new Win32Exception( Marshal.GetLastWin32Error() );
        }
        else
            throw new Win32Exception( Marshal.GetLastWin32Error() );
        return data;
    }
 
    /// <summary>Converts DER binary format to a CAPI CERT_PUBLIC_KEY_INFO structure containing an RSA key.</summary>
    internal static byte[] GetRSAFromDER( byte[] DERData )
    {
        byte[] data = null;
        byte[] publicKey = null;
        CERT_PUBLIC_KEY_INFO info;
        UInt32 dwCertPublicKeyInfoSize = 0;
        IntPtr pCertPublicKeyInfo = IntPtr.Zero;
        if ( RSACryptoServiceProviderExtension.CryptDecodeObject( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.X509_PUBLIC_KEY_INFO ),
            DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwCertPublicKeyInfoSize ) )
        {
            data = new byte[ dwCertPublicKeyInfoSize ];
            if ( RSACryptoServiceProviderExtension.CryptDecodeObject( CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr( (int)CRYPT_OUTPUT_TYPES.X509_PUBLIC_KEY_INFO ),
                DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwCertPublicKeyInfoSize ) )
            {
                GCHandle handle = GCHandle.Alloc( data, GCHandleType.Pinned );
                try
                {
                    info = (CERT_PUBLIC_KEY_INFO)Marshal.PtrToStructure( handle.AddrOfPinnedObject(), typeof( CERT_PUBLIC_KEY_INFO ) );
                    publicKey = new byte[ info.PublicKey.cbData ];
                    Marshal.Copy( info.PublicKey.pbData, publicKey, 0, publicKey.Length );
                }
                finally
                {
                    handle.Free();
                }
            }
            else
                throw new Win32Exception( Marshal.GetLastWin32Error() );
        }
        else
            throw new Win32Exception( Marshal.GetLastWin32Error() );
        return publicKey;
    }
 
    /// <summary>Extracts the binary data from a PEM file.</summary>
    internal static byte[] GetDERFromPEM( string sPEM )
    {
        UInt32 dwSkip, dwFlags;
        UInt32 dwBinarySize = 0;
 
        if ( !RSACryptoServiceProviderExtension.CryptStringToBinary( sPEM, (UInt32)sPEM.Length, CRYPT_STRING_FLAGS.CRYPT_STRING_BASE64HEADER, null, ref dwBinarySize, out dwSkip, out dwFlags ) )
            throw new Win32Exception( Marshal.GetLastWin32Error() );
 
        byte[] decodedData = new byte[ dwBinarySize ];
        if ( !RSACryptoServiceProviderExtension.CryptStringToBinary( sPEM, (UInt32)sPEM.Length, CRYPT_STRING_FLAGS.CRYPT_STRING_BASE64HEADER, decodedData, ref dwBinarySize, out dwSkip, out dwFlags ) )
            throw new Win32Exception( Marshal.GetLastWin32Error() );
        return decodedData;
    }
 
    #endregion Methods
 
    #region P/Invoke Constants
 
    /// <summary>Enumeration derived from Crypto API.</summary>
    internal enum CRYPT_ACQUIRE_CONTEXT_FLAGS : uint
    {
        CRYPT_NEWKEYSET = 0x8,
        CRYPT_DELETEKEYSET = 0x10,
        CRYPT_MACHINE_KEYSET = 0x20,
        CRYPT_SILENT = 0x40,
        CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x80,
        CRYPT_VERIFYCONTEXT = 0xF0000000
    }
 
    /// <summary>Enumeration derived from Crypto API.</summary>
    internal enum CRYPT_PROVIDER_TYPE : uint
    {
        PROV_RSA_FULL = 1
    }
 
    /// <summary>Enumeration derived from Crypto API.</summary>
    internal enum CRYPT_DECODE_FLAGS : uint
    {
        NONE = 0,
        CRYPT_DECODE_ALLOC_FLAG = 0x8000
    }
 
    /// <summary>Enumeration derived from Crypto API.</summary>
    internal enum CRYPT_ENCODING_FLAGS : uint
    {
        PKCS_7_ASN_ENCODING = 0x00010000,
        X509_ASN_ENCODING = 0x00000001,
    }
 
    /// <summary>Enumeration derived from Crypto API.</summary>
    internal enum CRYPT_OUTPUT_TYPES : int
    {
        X509_PUBLIC_KEY_INFO = 8,
        RSA_CSP_PUBLICKEYBLOB = 19,
        PKCS_RSA_PRIVATE_KEY = 43,
        PKCS_PRIVATE_KEY_INFO = 44
    }
 
    /// <summary>Enumeration derived from Crypto API.</summary>
    internal enum CRYPT_STRING_FLAGS : uint
    {
        CRYPT_STRING_BASE64HEADER = 0,
        CRYPT_STRING_BASE64 = 1,
        CRYPT_STRING_BINARY = 2,
        CRYPT_STRING_BASE64REQUESTHEADER = 3,
        CRYPT_STRING_HEX = 4,
        CRYPT_STRING_HEXASCII = 5,
        CRYPT_STRING_BASE64_ANY = 6,
        CRYPT_STRING_ANY = 7,
        CRYPT_STRING_HEX_ANY = 8,
        CRYPT_STRING_BASE64X509CRLHEADER = 9,
        CRYPT_STRING_HEXADDR = 10,
        CRYPT_STRING_HEXASCIIADDR = 11,
        CRYPT_STRING_HEXRAW = 12,
        CRYPT_STRING_NOCRLF = 0x40000000,
        CRYPT_STRING_NOCR = 0x80000000
    }
 
    #endregion P/Invoke Constants
 
    #region P/Invoke Structures
 
    /// <summary>Structure from Crypto API.</summary>
    [StructLayout( LayoutKind.Sequential )]
    internal struct CRYPT_OBJID_BLOB
    {
        internal UInt32 cbData;
        internal IntPtr pbData;
    }
 
    /// <summary>Structure from Crypto API.</summary>
    [StructLayout( LayoutKind.Sequential )]
    internal struct CRYPT_ALGORITHM_IDENTIFIER
    {
        internal IntPtr pszObjId;
        internal CRYPT_OBJID_BLOB Parameters;
    }
 
    /// <summary>Structure from Crypto API.</summary>
    [StructLayout( LayoutKind.Sequential )]
    struct CRYPT_BIT_BLOB
    {
        internal UInt32 cbData;
        internal IntPtr pbData;
        internal UInt32 cUnusedBits;
    }
 
    /// <summary>Structure from Crypto API.</summary>
    [StructLayout( LayoutKind.Sequential )]
    struct CERT_PUBLIC_KEY_INFO
    {
        internal CRYPT_ALGORITHM_IDENTIFIER Algorithm;
        internal CRYPT_BIT_BLOB PublicKey;
    }
 
    #endregion P/Invoke Structures
 
    #region P/Invoke Functions
 
    /// <summary>Function for Crypto API.</summary>
    [DllImport( "advapi32.dll", SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptDestroyKey( IntPtr hKey );
 
    /// <summary>Function for Crypto API.</summary>
    [DllImport( "advapi32.dll", SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptImportKey( IntPtr hProv, byte[] pbKeyData, UInt32 dwDataLen, IntPtr hPubKey, UInt32 dwFlags, ref IntPtr hKey );
 
    /// <summary>Function for Crypto API.</summary>
    [DllImport( "advapi32.dll", SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptReleaseContext( IntPtr hProv, Int32 dwFlags );
 
    /// <summary>Function for Crypto API.</summary>
    [DllImport( "advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptAcquireContext( ref IntPtr hProv, string pszContainer, string pszProvider, CRYPT_PROVIDER_TYPE dwProvType, CRYPT_ACQUIRE_CONTEXT_FLAGS dwFlags );
 
    /// <summary>Function from Crypto API.</summary>
    [DllImport( "crypt32.dll", SetLastError = true, CharSet = CharSet.Auto )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptStringToBinary( string sPEM, UInt32 sPEMLength, CRYPT_STRING_FLAGS dwFlags, [Out] byte[] pbBinary, ref UInt32 pcbBinary, out UInt32 pdwSkip, out UInt32 pdwFlags );
 
    /// <summary>Function from Crypto API.</summary>
    [DllImport( "crypt32.dll", SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptDecodeObjectEx( CRYPT_ENCODING_FLAGS dwCertEncodingType, IntPtr lpszStructType, byte[] pbEncoded, UInt32 cbEncoded, CRYPT_DECODE_FLAGS dwFlags, IntPtr pDecodePara, ref byte[] pvStructInfo, ref UInt32 pcbStructInfo );
 
    /// <summary>Function from Crypto API.</summary>
    [DllImport( "crypt32.dll", SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    internal static extern bool CryptDecodeObject( CRYPT_ENCODING_FLAGS dwCertEncodingType, IntPtr lpszStructType, byte[] pbEncoded, UInt32 cbEncoded, CRYPT_DECODE_FLAGS flags, [In, Out] byte[] pvStructInfo, ref UInt32 cbStructInfo );
 
    #endregion P/Invoke Functions
}

I am attaching a Visual Studio 2012 solution containing a sample usage for PEM and DER encoded files. Click here to download.

Solution containing sample keys:

Test program output:

OutputDebugString with Variable Arguments

May 11th, 2010 No comments

Just working with the new Visual Studio 2010, integrating Win32 console based code into a window app. The old code used fprintf(…) for debug output.
Since stdout/stderr is not available in a windows app, the fprintf based debug output had to be changed to print to the visual studio output window.

This can be accomplished via the universal OutputDebugString() API. The only shortcoming is, that it does not support variable arguments in a sprintf(…) form.

Here is a workaround:

#include <strsafe.h>
// ...
void MyOutputDebugString( LPCTSTR sFormat, ... )
{
    va_list argptr;      
    va_start( argptr, sFormat ); 
    TCHAR buffer[ 2000 ];
    HRESULT hr = StringCbVPrintf( buffer, sizeof( buffer ), sFormat, argptr );
    if ( STRSAFE_E_INSUFFICIENT_BUFFER == hr || S_OK == hr )
        OutputDebugString( buffer );
    else
        OutputDebugString( _T("StringCbVPrintf error.") );
}

I am using strsafe.h in this case to offer protection against buffer overruns. In case the internal buffer is not big enough to handle the output string, it will be safely truncated with an ending \0. In case you cannot make use of strsafe.h, here is an old style solution:

void MyOutputDebugString( LPCTSTR sFormat, ... )
{
    va_list argptr;      
    va_start( argptr, sFormat ); 
    TCHAR buffer[ 2000 ];
    wvsprintf( buffer, sizeof( buffer ), sFormat, argptr );
    buffer[ ( sizeof( buffer ) / sizeof( *buffer ) ) - 1 ] = '\0';
    OutputDebugString( buffer );
}