Developing with the ICardInitialization Interface

This topic includes a sample implementation of theForefront Identity Manager Certificate Management (FIM CM) ICardInitialization interface, with discussion of each method. For more information on the requirements needed to develop an assembly implementing ICardInitialization, see Introduction to ICardInitialization.

Programming Model

To provide custom smart card initialization, you will need to create a class which implements the ICardInitialization interface, and implements its methods.

Object Lifetime

FIM CM instantiates objects of your custom class once, when the FIM CM server instance is first created by IIS. Your custom object instance will be maintained throughout the lifetime of the FIM CM server process, and will be destroyed when the FIM CM process is recycled by IIS.

When FIM CM starts up, it calls your implementation's Initialize method with the default administrative key and a custom string, both of which are configured in the profile template configuration.

Note

For more information on configuring profile templates, see https://go.microsoft.com/fwlink/?LinkId=89698

This custom string is opaque to FIM CM, and can be used to pass custom data to your custom initialization object, such as information about connectivity to a custom hardware module.

Request Processing

The appropriate method in your custom object will be called each time the corresponding smart-card workflow step in the FIM CM is triggered. For this reason, each of your method implementations must be thread-safe and fully re-entrant.

Each of these processing steps takes place on threads managed by FIM CM. Any delays imposed by your object will add to the time needed for the server to issue a response to a client's request, and any blocking will block client HTTP requests from being processed, leading to slow-downs and even connection time-outs.

Helper Classes

In addition to ICardInitialization, the Microsoft.Clm namespace provides helper classes to aid with the card initialization process.

ICardContext

A read-only object of type ICardContext is passed to several ICardInitialization methods in order to provide them with the smart card's answer-to-reset (ATR) string and unique card ID.

SymmetricKey

Custom implementations of smart-card key encryption can derive from this abstract base class.

CustomCardInitializer Sample

Tailspin Toys has developed an assembly that implements ICardInitialization in order to provide their own custom smart-card initialization procedures. The source for this assembly, named CustomCardInitializer, can be found below.

using System;
using System.IO;
using System.Security.Cryptography;
using Microsoft.Clm;

namespace SampleCardInitializer
{

    public class CustomCardInitializer : ICardInitialization
    {
        private bool initialized;

        /// <summary>
        /// Initialize the provider with configuration data from the
        /// profile template.
        /// This must be called before any other method.
        /// </summary>
        /// <param name="configData">from the "Smart card initialization
        /// provider data:" field of the profile template</param>
        /// <param name="defaultAdminKeyData">from the "Admin key initial 
        /// value (hex):" field of the profile template"</param>
        public void Initialize(string configData, string defaultAdminKeyData)
        {
            // Ignore redundant calls.
            if (initialized)
                return;

            // Extract any necessary data here from the configuration
            // data and default admin key.
            // In this sample, we ignore the contents of these fields
            // for simplicity.
            initialized = true;
        }

        /// <summary>
        ///  
        /// </summary>
        /// <param name="requestUuid"></param>
        /// <returns>a unique ID for a smart card</returns>
        public Guid GenerateCardId(Guid requestUuid)
        {
            if (!initialized)
                throw new InvalidOperationException("Cannot generate ID for uninitialized card!");
            return System.Guid.NewGuid();
        }

        /// <summary>
        /// Compute the correct response to a Base CSP card challenge.
        /// </summary>
        /// <param name="card">context for the smart card currently
        /// in the reader</param>
        /// <param name="challenge">an 8-byte array provided by 
        /// the card</param>
        /// <param name="useDiversifiedKey"></param>
        /// <returns>the challenge encrypted using Triple DES with 
        /// the appropriate (default or diversified) admin key</returns>
        public byte[] GetCardChallengeResponse(
                    ICardContext card,
                    byte[] challenge,
                    bool useDiversifiedKey)
        {
            SymmetricKey adminKey = useDiversifiedKey ? GetDiversifiedAdminKey(card) : DefaultAdminKey;

            return adminKey.Encrypt(challenge,
                                    new byte[8]); 
                                   // use 0's for the initialization vector
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="card">context for the smart card currently in the reader</param>
        /// <returns></returns>
        public SymmetricKey GetDiversifiedAdminKey(ICardContext card)
        {
            if (!initialized)
                throw new InvalidOperationException("Class must be initialized before calling the GetDiversifiedAdminKey method");

            // This sample is for educational purposes only. We hardcode 
            // a value here for simplicity, but a proper implementation 
            // should derive a unique 24-byte key based on properties of 
            // the card such as card ID.
            byte[] diversifiedKey = new byte[] {0x10, 0x20, 0x30, 0x10, 0x20, 0x30, 0x10, 0x20, 0x30, 0x10, 0x20, 0x30, 0x10, 0x20, 0x30, 0x10,                                 0x20, 0x30, 0x10, 0x20, 0x30, 0x10, 0x20, 0x30};

            return new TripleDesSymmetricKey(diversifiedKey);
        }

        /// <summary>
        /// Gets the default admin key for this card.
        /// If you need to select the correct admin key from a list of card 
        /// ID/admin key pairs, you will need to modify the code
        //  to make that lookup information available to this class
        /// </summary>
        public SymmetricKey DefaultAdminKey
        {
            get
            {
                if (!initialized)
                    throw new InvalidOperationException("Cannot get default admin key for uninitialized card!");

                // We hard code a constant value here for simplicity.
                byte[] defaultAdminKey = new byte[] {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90};

                return new TripleDesSymmetricKey(defaultAdminKey);
            }
        }
    }


    public class TripleDesSymmetricKey : SymmetricKey
    {
        private byte[] encryptKey;

        public TripleDesSymmetricKey(byte[] key)
        {
            if (key == null || key.Length != 24)
            {
                throw new ArgumentNullException("Invalid key.");
            }

            // Copy the contents of the array to avoid external tampering.
            encryptKey = new byte[key.Length];
            key.CopyTo(encryptKey, 0);
        }

        public override byte[] Key
        {
            get
            {
                return encryptKey;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="data">Base CSP challenge to be encrypted</param>
        /// <param name="iv">Initialization vector, may be null.
        /// If specified, the length of the array must be equal to
        /// the Triple DES block size (8 bytes).</param>
        /// <returns>the challenge encrypted with Triple DES using 
        /// the given IV</returns>
        public override byte[] Encrypt(byte[] data, byte[] iv)
        {
            if (data == null)
            {
                throw new ArgumentNullException("Data to encrypt may not be null.");
            }

            if (data.Length != 8)
            {
                throw new ArgumentException("Challenge to encrypt must be 8 bytes long.");
            }

            if (iv != null && iv.Length != 8)
            {
                throw new ArgumentException("Initialization vector must be 8 bytes long.");
            }

            TripleDES TripleDesAlg = TripleDES.Create();

            // CM expects the encrypted data to be the same size
            // as the input data
            TripleDesAlg.Padding = PaddingMode.None;  
            TripleDesAlg.Key = encryptKey;

            if (iv != null)
            {
                TripleDesAlg.IV = iv;
            }

            MemoryStream memStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memStream, TripleDesAlg.CreateEncryptor(), CryptoStreamMode.Write);

            cryptoStream.Write(data, 0, data.Length);
            cryptoStream.FlushFinalBlock();

            // memStream contains the encrypted data in byte format
            return memStream.ToArray();
        }

        public override void Dispose()
        {
            // Dispose of unmanaged resources.
        }
    }

}

See Also

Reference

ICardInitialization

Concepts

Introduction to ICardInitialization

Other Resources

FIM CM Customizable Interfaces Overview