Cryptography in .NET

Overview

Windows provides a Public Key Infrastructure (PKI) that allows us to store
certificates for encryption purposes. To access the this store we had two
possibilities in the unmanaged past: The CryptoAPI and the CAPICOM.

The Framework Class Library (= FCL) provies a lot of function
for encryption, but the current release doesn't provide any classes for
accessing the the certificate store. Fortunately a the
Web Services Development Kit (WSDK) Technology Preview adds this
functionality; later this will be included in the FCL itself. But in the mean
time you have to download the WSDK.

Asymmetric Encryption/Signature Example

The following code is the funcionallity of my submitted example. It assumes
that you have at least two certificates (with private key!) in your Personal
Certificate Store.

try
{
// GENERAL CODE TO READ THE CERTIFICATES FROM THE WINDOWS PKI INFRASTRUCTURE//// BEGINNER-TIP: Start MMC (=Microsoft Management Console) and select "Add-in/remove Snapin"// from the "Console" menu. Now press "Add.." button. Select "Certificates" in the list and// press "Add" button. You have the choise to select "My user account" or "Computer account".// Then press "Finish" and "Close" and start exploring the installed certificates... // Each store has a "Personal" section with is BTW represented by the letters "MY".// Also interessting is the "Trusted root" certificates, there you see all the Certificate// Issuer that you trust, there is quite a lot and sometimes it's a good idea to delete// all of them and only add the one you need or really trust, for security resasons.//// Open private certificate store of current user
X509CertificateStore store =
X509CertificateStore.CurrentUserStore( X509CertificateStore.MyStore );
store.OpenRead();
// Read e.g. the first two certificate
X509Certificate sender = (X509Certificate)store.Certificates[0];
X509Certificate receiver = (X509Certificate)store.Certificates[1];
// Let's see who we are dealing with... - ps: not nessesary for the following codestring sender_serial = sender.GetName();
string receiver_serial = receiver.GetName();
//// SENDER-SIDE CODE//// SENDER-SIDE: Extract own private keys and receiver's public key
RSAParameters sender_private = sender.Key.ExportParameters( true );
RSAParameters receiver_public = receiver.Key.ExportParameters( false );
// SENDER-SIDE: Asymmetric encryption with receivers's public key
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters( receiver_public );
byte[] cleartext = ASCIIEncoding.ASCII.GetBytes("test");
byte[] cipher = rsa.Encrypt( cleartext, false );
// SENDER-SIDE: Sign the cipher with own private key
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters( sender_private );
byte[] signature = rsa.SignData( cipher, new SHA1CryptoServiceProvider() );
//// TODO: TRANSFER DATA OVER UNSECURE CHANNEL...//// RECEIVER-SIDE: Get own private key and sender's public key
RSAParameters receiver_private = receiver.Key.ExportParameters( true );
RSAParameters sender_public = sender.Key.ExportParameters( false );
// RECEIVER-SIDE: Verify signature with sender's public key//// Note: You are ONLY verifying the signature and NOT verifying the Certificate!// It's corresponding to the CAPICOM call SignedData.Verify( CAPICOM_VERIFY_SIGNATURE_ONLY )// I did not yet find out how we can use the .NET library to verify the Certificate// against the issuer-chain. If someone knows how to do this, and not using interop// and the SignedData.Verify( CAPICOM_VERIFY_SIGNATURE_AND_CERTIFICATE ), I would be// very, very, very happy - because this is a requirement in the software I'm developing// currently and if we can't do that I have to do CAPI-interop :-(// At the moment I think that there is no simple function call for this and this s***s.// Maybe it's possible to walk throw the chain-of-issuers and validate the fingerprint// this the public key of the issuer. But I don't know enough about that....// ANY HELP TO THIS POINT IS MORE THAN WELCOME//
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters( sender_public );
if( rsa.VerifyData( cipher, new SHA1CryptoServiceProvider(), signature ) )
{
// RECEIVER-SIDE: Asmymetirc decryption with own private key
rsa.ImportParameters( receiver_private );
byte[] cleartext_after_decription = rsa.Decrypt( cipher, false );
// Check result
Debug.Assert( ASCIIEncoding.ASCII.GetString( cleartext ) ==
ASCIIEncoding.ASCII.GetString( cleartext_after_decription ),
"Ups, the cleartext input is not equal the cleartext output..." );
}
else
Debug.Assert( false, "Ups, check signature failed!" );
}
catch( Exception e )
{
// NOTE: the following exception, that may occure during 'ExportParameters( true )'//// System.Security.Cryptography.CryptographicException// "Key information could not be exported from the cryptographic service // provider (CSP) for this implementation." }"//// Can have one of the following reasons:// + The certificate was NOT imported with the flag "Mark the private key as exportable"// + The type "SSL Server Authentication(40)" and is in a CurrentUser store and not in// the LocalComputer store. See certificate details in the MMC under "NetscapeCertType".// IMHO: This reason is very wicked and I don't understand it!!
Debug.Assert( false, e.ToString() );
}

Note: The X509Certificate class provides the function ExportParameters and the
boolean parameter defines if the private key has to be submitted as well.

A quick note

Favorite person names for encryption examples are Alice, Bob and mean
Steve. If you like to see how they are doing in .NET, search your
MSDN examples
for the excellent example file called PublicKey.cs!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

I have a Windows Service that receives Documents(files), Digitaly Sign them and return the signature. The Signature is computed using a SmartCard Key (Hardware).

So, in the Service, I select the CSP (Schlumberger Cryptographic Service Provider) and the container name I need.

- It works fine while running this Service as Local System Account.

For several "good reasons" (client needs), this service must be run under a Local Administrator account.

- Doing so, I am able to verify the signature, BUT not Signing. (This also means using the public Key works, using the private key doesnt...) (signature works in this config on XP , not on Windows Server 2000/2003 )

- Converting this service to a normal application and running it while logged with the same Local Administrator account ->> it works.

So my question is: What should I do to have this Service working with Admin on a WinServer2003 ??

The Windows Service does not have an profile loaded, therefore the encryption rotines (RSA, etc) do not work. Assign the service a user account and make sure the profile get's loaded. (See for an example of all this the MSDN library Data Protection Api sample)