How to authenticate with smart cards and virtual smart cards (HTML)

Starting with Windows 8.1 your app can communicate with smart card readers and connected smart cards, or create virtual smart cards, to authenticate users for access to secure network services. The APIs that make this possible are defined in the Windows.Devices.SmartCards namespace.

This topic will show you how to access a physical smart card or create a new virtual smart card. Then

What you need to know

Technologies

Prerequisites

This topic and the provided code examples directly reference the Windows 8.1 Smart card sample. In addition to referencing the new Windows.Devices.SmartCards namespace, the creation of admin keys for virtual smart cards also includes some APIs defined by the Windows.Security.Cryptography namespace.

Important  Before your Windows Store app development can authenticate users using smart cards or virtual smart cards, you must set the Shared User Certificates capability in the project Package.appxmanifest file.

 

Access connected card readers and smart cards

The GetDeviceSelector method returns a string representing all smart card readers connected to the device, and is used as the argument in the FindAllAsync call that retrieves these devices.

To access the smart cards currently attached to a returned reader device, call SmartCardReader.FindAllCardsAsync.

var selector =
    Windows.Devices.SmartCards.SmartCardReader.getDeviceSelector();
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(
    selector,
    null).then(
function (devices) {
    return WinJS.Promise.join(devices.map(
    function (device) {
        return Windows.Devices.SmartCards.SmartCardReader
               .fromIdAsync(device.id);
    }));
}).then(
function (readers) {
    return WinJS.Promise.join(readers.map(
    function (reader) {
        return reader.findAllCardsAsync();
    }))
});

You can then pass each returned SmartCard object to SmartCardProvisioning to access the methods that allow your app to access and customize its configuration.

You should also enable your app to observe for CardAdded and events and implement a function to determine app behavior on card insertion.

For example, for CardAdded events, to create a reference to a SmartCardReader object:

Sample.reader =
    smartCardReader;
Sample.reader.oncardadded = handleCardAdded;

function handleCardAdded(eventArgs) {
    // App behavior on card insertion
}

Create a virtual smart card

To create a virtual smart card using SmartCardProvisioning, your app will first need to provide a friendly name, an admin key, and a SmartCardPinPolicy. The friendly name is generally something provided to the app, but your app will still need to provide an admin key and generate an instance of the current SmartCardPinPolicy before passing all three values to RequestVirtualSmartCardCreationAsync.

  1. Create a new instance of a SmartCardPinPolicy
  2. Generate the admin key value by calling CryptographicBuffer.GenerateRandom on the admin key value provided by the service or management tool.
  3. Pass these values along with the FriendlyNameText string to RequestVirtualSmartCardCreationAsync.
var adminKey = Windows.Security.Cryptography.CryptographicBuffer
               .generateRandom(Sample.ADMIN_KEY_LENGTH_IN_BYTES);

var pinPolicy = null;
try {
    pinPolicy = parsePinPolicy();
}
catch (error) {

    //Inform user of error in generation of PinPolicy

    return;
}

Now pass these values along with the FriendlyNameText string to RequestVirtualSmartCardCreationAsync.

Windows.Devices.SmartCards.SmartCardProvisioning
.requestVirtualSmartCardCreationAsync(
    document.getElementById("FriendlyNameText").value,
    adminKey,
    pinPolicy).then(
function (smartCardProvisioning) {

    // If the user selects Cancel on the card creation prompt, a null value
    // will be returned. In this case, cancel the asynchronous call.
    if (null === smartCardProvisioning) {

        // Return the static, pre-canceled promise.
        return WinJS.Promise.cancel;
    }
}

Once RequestVirtualSmartCardCreationAsync has returned the associated SmartCardProvisioning object, the virtual smart card is provisioned and ready for use.

Note  To re-provision a virtual smart card your app should retrieve the challenge context from GetChallengeContext object and use it to re-provision the card with SmartCardChallengeContext.ProvisionAsync. This topic will go over what your app needs to do to support authentication challenges in the following steps.

 

Handle authentication challenges

To authenticate with smart cards or virtual smart cards, your app must provide the behavior to complete challenges between the admin key data stored on the card, and the admin key data maintained by the authentication server or management tool.

The following code is an example of how to support smart card authentication for services or modification of physical or virtual card details. If the data generated using the admin key on the card ("challenge") is the same as the admin key data provided by the server or management tool ("adminkey"), authentication is successful.

function calculateChallengeResponse(challenge, adminkey) {
    var objAlg =
        Windows.Security.Cryptography.Core.SymmetricKeyAlgorithmProvider
        .openAlgorithm(
            Windows.Security.Cryptography.Core.SymmetricAlgorithmNames
            .tripleDesCbc);

    var symetricKey = objAlg.createSymmetricKey(adminkey);
    var buffEncrypted =
        Windows.Security.Cryptography.Core.CryptographicEngine.encrypt(
        symetricKey,
        challenge,
        null);

    return buffEncrypted;
};

You will see this code referenced throughout the remainder of this topic was we review how to complete an authentication action, and how to apply changes to smart card and virtual smart card information.

Verify smart card or virtual smart card authentication response

Now that we have the logic for authentication challenges defined, we can communicate with the reader to access the smart card, or alternatively, access a virtual smart card for authentication.

  1. To begin the challenge, call GetChallengeContextAsync from the SmartCardProvisioning object associated with the smart card. This will generate an instance of SmartCardChallengeContext, which contains the card's Challenge value.

  2. Next, pass the card's challenge value and the admin key provided by the service or management tool to the ChallengeResponseAlgorithm that we defined in the previous example.

  3. VerifyResponseAsync will return true if authentication is successful.

Sample.getSmartCard().then(
function (smartCard) {
    return Windows.Devices.SmartCards.SmartCardProvisioning
           .fromSmartCardAsync(smartCard);
}).then(
function (smartCardProvisioning) {
    return smartCardProvisioning.getChallengeContextAsync();
}).then(
function (challengeContext) {
    var response = Sample.calculateChallengeResponse(
        challengeContext.challenge,
        Sample.adminKey);
    return challengeContext.verifyResponseAsync(response);
}).done(
function (result) {
    //Indicate successful authentication
},
function (error) {
    //Indicate unsuccessful authentication
});

Change or reset a user PIN

To change the PIN associated with a smart card:

  1. Access the card and generate the associated SmartCardProvisioning object.
  2. Call RequestPinChangeAsync to display a UI to the user to complete this operation.
  3. If the PIN was successfully changed the call will return true.
Sample.getSmartCard().then(
function (smartCard) {
    return Windows.Devices.SmartCards.SmartCardProvisioning
           .fromSmartCardAsync(smartCard);
}).then(
function (smartCardProvisioning) {
    return smartCardProvisioning.requestPinChangeAsync();
}).done(
function (result) {
    if (result == true) {
        //Indicate that the PIN change was successful
    }
    else {
        //Indicate that the PIN reset was unsuccessful
    }
});

To request a PIN reset:

  1. Call RequestPinResetAsync to initiate the operation. This call includes a SmartCardPinResetHandler method that represents the smart card and the pin reset request.

  2. SmartCardPinResetHandler provides information that our ChallengeResponseAlgorithm, wrapped in a SmartCardPinResetDeferral call, uses to compare the card's challenge value and the admin key provided by the service or management tool to authenticate the request.

  3. If the challenge is successful, the RequestPinResetAsync call is completed; returning true if the PIN was successfully reset.


Sample.getSmartCard().then(
function (smartCard) {
    return Windows.Devices.SmartCards.SmartCardProvisioning
           .fromSmartCardAsync(smartCard);
}).then(
function (smartCardProvisioning) {
    return smartCardProvisioning.requestPinResetAsync(
    function (sender, request) {
        var deferral = request.getDeferral();
        var response = SdkSample.calculateChallengeResponse(
            request.challenge,
            Sample.adminKey);

        request.setResponse(response);

        deferral.complete();
    });
}).done(
function (result) {
    if (result == true) {
        //Indicate that the PIN reset was successful
    }
    else {
        //Indicate that the PIN reset was unsuccessful
    }
});

Remove a smart card or virtual smart card

When a physical smart card is removed a CardRemoved event will fire when the card is deleted.

Associate the firing of this event with the card reader with the method that defines your app's behavior on card or reader removal as an event handler. This behavior can be something as simply as providing notification to the user that the card was removed.

Sample.getSmartCard().then(
function (smartCard) {
    SdkSample.reader.oncardremoved = handleCardRemoved;
    }).done(
function (result) {
    //Indicate that the smart card was removed 
    //and clean up any app associations with that card
    Sample.reader = null;
},
function (error) {
    //Indicate that there was an error when removing
    //the smart card reference from the app
});

The removal of a virtual smart card is handled programmatically by first retrieving the card and then calling RequestVirtualSmartCardDeletionAsync from the SmartCardProvisioning returned object.


Sample.getSmartCard().then(
function (smartCard) {
    return Windows.Devices.SmartCards.SmartCardProvisioning
           .requestVirtualSmartCardDeletionAsync(smartCard);
}).done(
function (result) {
    //Indicate that the virtual smart card was successfully deleted.
},
function (error) {
    //Indicate that there was an error when deleting the virtual smart card.
});