Introduction to digital signatures in .NET cryptography

Introduction

I’ve devoted the last few posts to cryptography in .NET: hashing, asymmetric and symmetric encryption. Digital signatures are yet another application of cryptography. They provide data integrity and non-repudiation. This latter means that a user cannot claim that he or she wasn’t the one how signed a particular – digital – document. As public keys can be distributed to other users for encryption purposes they are not good candidates for ensuring non-repudiation. So we need to use the sender’s private key with which the contents of a message can be signed. The contents will be hashed. “Signing” means encrypting with the sender’s private key.

Here’s a flow diagram of the process:

Digital signatures flow

In this scenario we’d like to send some information to another person in a secure way. We want to encrypt it so that only that person can see it and we also want to sign that message so that the receiver will know that it came from us.

The message we want to send is shown on the left of the diagram as “plain text”. The next step is the same as what we saw in asymmetric encryption: we encrypt the message with the receiver’s public key. We get the cipher text as the result. We then hash that cipher text so that we know that it won’t be tampered with while it’s transmitted to the receiver. In addition it’s not necessary to sign the entire data content as it can potentially grow very large and asymmetric encryption is rather slow – we don’t want to waste a lot of time just signing large size data.

So instead of signing the actual cipher text we get the hash of it, we encrypt the hash using our own private key. The result is a digital signature and a cipher text. The cipher can only be encrypted by the receiver using their private key. On top of that the hash is signed with our private key. The receiver can decrypt the signature using our public key since – recall from the post on asymmetric encryption – the private and public keys are inverses of each other. The receiver will have access to our public key. If the decryption succeeds then the receiver will know that it must have come from someone who has the correct private key.

This all probably sounds a bit complex at first but I hope the demo will make it clear how it all works.

The creation of the cipher text is not actually required for digital signatures. The main purpose of digital signatures is tamper-proof messaging. In the demo we’ll go for the full encryption cycle – if you don’t require the encryption bit in your code then you can simply ignore it.

Demo

Create a new Console app in Visual Studio. Insert a class called Sender. Add two class level fields to Sender.cs:

private string _myRsaKeys = "...";
private string _receiversPublicKey = "...";

I’ve shown you some techniques in the previous post on how to generate valid RSA keys, I won’t repeat it here. The important thing here is that _myRsaKeys will be an XML string including both my private and public keys, so copy the entire XML contents there. Then generate another set of RSA keys and save the public key portion of it in _receiversPublicKey, but don’t throw away the rest, it will be needed later when we look at the Receiver. Again, refer to the previous post to see how to keep the private and public key portions of an RSA key pair in an XML string variable. This is a simulation of a case where I received the public key of a partner whome I wish to send encrypted messages.

We’ll need RSA ciphers for the sender and receiver so add the following methods to Sender.cs:

private RSACryptoServiceProvider GetSenderCipher()
{
	RSACryptoServiceProvider sender = new RSACryptoServiceProvider();
	sender.FromXmlString(_myRsaKeys);
	return sender;
}

private RSACryptoServiceProvider GetReceiverCipher()
{
	RSACryptoServiceProvider sender = new RSACryptoServiceProvider();
	sender.FromXmlString(_receiversPublicKey);
	return sender;
}

This should look familiar from the previous post. We’ll use our own private key in the GetSenderCipher method for creating the signature – if you get lost you can always refer back to the diagram above. The receiver’s public key will be needed in order to encrypt the plain text and create the cipher text. They will use their private key to do the decryption.

We can compute the hash of the cipher text using the following method:

private byte[] ComputeHashForMessage(byte[] cipherBytes)
{
	SHA1Managed alg = new SHA1Managed();
	byte[] hash = alg.ComputeHash(cipherBytes);
	return hash;
}

This should look familiar from the post on hashing techniques in .NET. We’ll sign this hash instead of the entire data transmitted.

There’s a built in signature provider in .NET represented by the RSAPKCS1SignatureFormatter object which will come handy in the following method:

private byte[] CalculateSignatureBytes(byte[] hashToSign)
{
	RSAPKCS1SignatureFormatter signatureFormatter = new RSAPKCS1SignatureFormatter(GetSenderCipher());
	signatureFormatter.SetHashAlgorithm("SHA1");
	byte[] signature = signatureFormatter.CreateSignature(hashToSign);
	return signature;
}

The RSAPKCS1SignatureFormatter object then accepts an RSA provider to sign with which in this case will be our private key. We specify SHA1 as the hash algorithm for the signature. This is the signature that the receiver will verify using our public key.

The methods can be connected in the following public method:

public DigitalSignatureResult BuildSignedMessage(string message)
{
	byte[] messageBytes = Encoding.UTF8.GetBytes(message);
	byte[] cipherBytes = GetReceiverCipher().Encrypt(messageBytes, false);
	byte[] cipherHash = ComputeHashForMessage(cipherBytes);
	byte[] signatureHash = CalculateSignatureBytes(cipherHash);

	string cipher = Convert.ToBase64String(cipherBytes);
	string signature = Convert.ToBase64String(signatureHash);
	return new DigitalSignatureResult() { CipherText = cipher, SignatureText = signature };
}

…where DigitalSignatureResult is a simple DTO:

public class DigitalSignatureResult
{
	public string CipherText { get; set; }
	public string SignatureText { get; set; }
}

The steps in the BuildSignedMessage correspond to the flow diagram: we encrypt the message, compute a hash of it and finally sign it.

Let’s test from Program.cs if it looks OK up to this point:

static void Main(string[] args)
{
	Sender sender = new Sender();
	DigitalSignatureResult res = sender.BuildSignedMessage("Hello digital sig!");
	Console.WriteLine(res.CipherText);
	Console.WriteLine(res.SignatureText);

	Console.ReadKey();
}

Run the programme and if everything went well then you should see two sets of character-jungles in the console window.

Now let’s see what the receiver looks like. Add a new class called Receiver. Insert the following class level private fields:

private string _myRsaKeys = "...";
private string _senderPublicKey = "...";

Here the values will be the inverses of the _myRsaKeys and _receiversPublicKey fields of Sender.cs. Receiver._myRsaKeys will be the full XML version of Sender._receiversPublicKey. Conversely Receiver._senderPublicKey will be the reduced public-key-only version of Sender._myRsaKeys. The sender’s public key will be used to verify their signature.

To make this clearer I have the following values in Receiver.cs:

private string _myRsaKeys = "<RSAKeyValue><Modulus>vU3Yfu1Z4nFknj9daoDmh+I0CzR+aLnTjUSejQyNJ0IgMb59x4mVe17C6U+bl4Cry7gXAk3LEmmE/BRxjlF8HKlXixoBWak1dpmr89Ye7iaD2UWwl5Dmn07Q9s27NGdywy0BsD1vDcFSgno3LUbVznkw/0hypbnOPxWKlBCao2c=</Modulus><Exponent>AQAB</Exponent><P>6veL+pbUjOr0PAiFcvBRwNlTz/+8T1iLHqkCggRPDSsTg25ybSqDa98mP5NQj9LHSYCECjOGZkiN4NoxgPPDxw==</P><Q>zj/l0Z36A/iD2IrVQzrEsvp31cmU6f9VCyPIGiM0FSEXbj23JuPNUPCzSo5oAAiSZfs/hR9uuAx1xQFAfTzjYQ==</Q><DP>dsW7VGh5+OGro80K6BbivIEfBL1ZCyLO8Ciuw9o5u4ZSztU9skETPawHQYvN5WW+p0D3fdCd14ZFcavZ6j1OcQ==</DP><DQ>YSQBRzgjsEkVOCEzjsWYLUAAvwWBiLCEyolgzsaz2hvK4FZa9AspAa1MlJn768Ady8CJS1bhm/fqZA5R5GqQIQ==</DQ><InverseQ>zEGFnyMtfxSYHwRv8nZ4xVcFctnU2pYmmXXYv8NV5FvhZi8Z1f1GE3tmS8qDyIuDTrXjmII2cffLMjPOVmLKoQ==</InverseQ><D>Ii97qDg+oijuDbHNsd0DRIix81AQf+MG9BzvMPOSTgOgAruuxSjwaK4NLsrkgzCGVayx4wWfZXzOuiMK+rN2YPr6IPeut3O14uuwLH7brxkit+MnhclsCtKpdT2iuUGOnbEhWccepCO7YLyyczhT9GE0rEtbEK6S7wvVKab/osE=</D></RSAKeyValue>";
		

private string _senderPublicKey = "<RSAKeyValue><Modulus>rW0Prd+S+Z6Wv0gEakgSp/v8Pu4xJ6OjaVCHKTIcf/C5nZvE77454lii3Ne6odV+76oaM2Pn3I9kKehK7CtqklI7rc1+05WRE3u8O5tC5v2ECjEDPMULAcZVTjXSyZtSAOiqk+6nEcJGRED65aGXwFgZuxEY8y4FbUma3I311aM=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

…and in Sender.cs:

private string _myRsaKeys = "<RSAKeyValue><Modulus>rW0Prd+S+Z6Wv0gEakgSp/v8Pu4xJ6OjaVCHKTIcf/C5nZvE77454lii3Ne6odV+76oaM2Pn3I9kKehK7CtqklI7rc1+05WRE3u8O5tC5v2ECjEDPMULAcZVTjXSyZtSAOiqk+6nEcJGRED65aGXwFgZuxEY8y4FbUma3I311aM=</Modulus><Exponent>AQAB</Exponent><P>5TYzDyoQBT4C8eqyuWlfNbg0XfnJAUHzonOiz/5az86E9y8V3oxDH3B3GMECDzvcLRJnp5x/G1Lectu1p3ckDw==</P><Q>wbHOTIh7l/p9FszFj/uMdvLlITyABeOZVJEPJhw6fkMSqiRqnx4F2dtqRcGUDBhpWbG6kbTXi9ijMVL8u+iRLQ==</Q><DP>h0KOqvo1bgKEFmJbiZKm/rpvHK3UcguLTGhUwczlpg/G419D1oqK6biib1cmcfrvGSHtTTnKwEMMxlblQafK/Q==</DP><DQ>u80hQFVouF+Xn16mA0eb1s0FWmdlndAin7sSHBpsoHV6CFvMwUCD3cp/TOk3GU8l/mBzi8jy4NYIzM8w2yTQdQ==</DQ><InverseQ>1rYDocFlo3EEs28Miieqa/fE8uzESz6YWONuZPoKHWO/1m9Tf0K01+TtPqDBFRhFBaTNKBJ2lyCGGRIEA41CYg==</InverseQ><D>dZvsciGYbqfZ20ZfmCPgYwNEAPlPZG5Yt2bhAlL1eN4rQnMMjvkWECXD7Lhv3KgIOUfGFOu/pZeoebMKfDbFQe6uA9f4jSYiC3yI0lyGiZQ+SpyJPRKetSSSqiOcK/vnnn2+03RgOVnyU3T52hRXVsb3oXtT5xacWm4IeGABB2E=</D></RSAKeyValue>";
		

private string _receiversPublicKey = "<RSAKeyValue><Modulus>vU3Yfu1Z4nFknj9daoDmh+I0CzR+aLnTjUSejQyNJ0IgMb59x4mVe17C6U+bl4Cry7gXAk3LEmmE/BRxjlF8HKlXixoBWak1dpmr89Ye7iaD2UWwl5Dmn07Q9s27NGdywy0BsD1vDcFSgno3LUbVznkw/0hypbnOPxWKlBCao2c=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

We’ll need to set up sender and receiver RSA providers in the Receiver class as well:

private RSACryptoServiceProvider GetSenderCipher()
{
	RSACryptoServiceProvider sender = new RSACryptoServiceProvider();
	sender.FromXmlString(_senderPublicKey);
	return sender;
}

private RSACryptoServiceProvider GetReceiverCipher()
{
	RSACryptoServiceProvider sender = new RSACryptoServiceProvider();
	sender.FromXmlString(_myRsaKeys);
	return sender;
}

We’ll also need the same hash computation method that was employed on the Sender’s side.

private byte[] ComputeHashForMessage(byte[] cipherBytes)
{
	SHA1Managed alg = new SHA1Managed();
	byte[] hash = alg.ComputeHash(cipherBytes);
	return hash;
}

I realise that this is a lot of duplication but imagine that the Sender and Receiver are different applications with no possibility to have a shared project. It’s important they they set up details such as “SHA1” in the ComputeHashForMessage methods for consistency. If one specifies SHA1 and the other one SHA256 then the process will fail of course so the two sides must agree on a common platform. Usually the person who needs to send a message to someone else will need to comply with what the receiver has set up on their side.

The RSAPKCS1SignatureDeformatter object has a VerifySignature method that is very useful in our case:

private void VerifySignature(byte[] computedHash, byte[] signatureBytes)
{
	RSACryptoServiceProvider senderCipher = GetSenderCipher();
	RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(senderCipher);
	deformatter.SetHashAlgorithm("SHA1");
	if (!deformatter.VerifySignature(computedHash, signatureBytes))
	{
		throw new ApplicationException("Signature did not match from sender");
	}
}

We’ll need to pass in the computed hash of the cipher text received from the sender and the signature bytes so that we can verify the authenticity of the message. If this method returns true then the receiver will know that the message must have originated from someone with the correct private key. Otherwise there’s reason to suspect that the message has been tampered with. In fact we need to recompute that hash of the cipher the same way the sender computed the hash. You can see that in the following public method in Receiver.cs:

public string ExtractMessage(DigitalSignatureResult signatureResult)
{
	byte[] cipherTextBytes = Convert.FromBase64String(signatureResult.CipherText);
	byte[] signatureBytes = Convert.FromBase64String(signatureResult.SignatureText);
	byte[] recomputedHash = ComputeHashForMessage(cipherTextBytes);
	VerifySignature(recomputedHash, signatureBytes);
	byte[] plainTextBytes = GetReceiverCipher().Decrypt(cipherTextBytes, false);
	return Encoding.UTF8.GetString(plainTextBytes);
}

This is the inverse of the BuildSignedMessage of Sender.cs. We send in the DigitalSignatureResult object that resulted from the encryption process on the sender’s side. The cipher and signature are converted back to byte arrays. Then the hash is recomputed and the signature is verified. If we get this far then we know that the message is authentic and we can decrypt the message.

The ExtractMessage can be called in Program.cs as follows:

static void Main(string[] args)
{
	Sender sender = new Sender();
	DigitalSignatureResult res = sender.BuildSignedMessage("Hello digital sig!");
	Console.WriteLine(res.CipherText);
	Console.WriteLine(res.SignatureText);

	String decryptedText = new Receiver().ExtractMessage(res);
	Console.WriteLine(decryptedText);

	Console.ReadKey();
}

Run the application and you should see that the signature is correct and the message is correctly decrypted by the receiver.

Digital certificates

We’ll wrap up this post on a short intro into digital certificates. Certificates are another, more advanced way to make sure that a message is coming from the person who claims to be the sender of the message. They are often used in conjunction with web traffic. If you see https:// in the URL then you know that some certificate is involved. In that case it’s not plain TCP that’s used for data transmission but SSL – Secure Sockets Layer – and TLS – Transport Layer Security.

A digital certificate is basically a public key assigned to a particular entity, like what a registration number is to a car. The number plate can be used to verify the owner of the car. The driver presents an ID card and if the data on the ID card matches what the police registry says about the owner of the car then the “certificate” is accepted. Otherwise the driver may have stolen the car or the number plate.

The format that a digital certificate follows is usually an X509 format. It contains an issuer, a validity period, the public key and the issuer’s signature. The digital signature helps us validate the public key of the sender. This process requires a certain level of trust in the issuer of the certificate. There are trusted issuers, so called Certificate Authorities (CAs), that the browsers have on their trusted issuers’ list.

It is the CA that signs the public key. They basically tell you like “we attest that the public key in this certificate comes from the owner of the public key”. The browser will have a list of those approved CAs and their public keys so they can validate signatures attached to these certificates.

So when the owner of the car presents an ID then it cannot just be any type of ID, it must have been issued by some authority. The authority attests through the ID that the person is really the one the ID claims them to be. This ties in well with claims-based security.

TLS and SSL are responsible to do the encryption between web browsers and web servers to make sure that the messages are not tampered with. You can use self-signed certificates for testing but if you use it in production then the web browser will warn you that the certificate didn’t come from one of the trusted CAs.

In other cases you may see that the browser warns that the certificate comes from a trusted CA, so the certificate is itself correct, but the domain name within the certificate is different from the domain name I’m visiting. It may be a sign that someone stole someone else’s certificate.

You can view the list of posts on Security and Cryptography here.

Symmetric algorithms in .NET cryptography part 2

In the previous post we started looking at symmetric encryption algorithms in .NET. We also saw an example on how the encrypt and decrypt a string value. We’ll wrap up the discussion with a couple of related topics.

Generating secret keys

You cannot just use any key in the encryption process, it must have a valid format with the specified bit size – usually either 128 or 256 bits. You can generate random bytes using the following function:

public void GenerateRandomBytes(byte[] buffer)
{
	RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
	rng.GetBytes(buffer);
}

We’ve seen the RNGCryptoServiceProvider object in the previous post. It helps generate cryptographically random numbers.

You can call this function as follows:

public string GetValidEncryptionKey(int bitSize)
{
	byte[] key = new byte[bitSize / 8];
	GenerateRandomBytes(key);
	return BitConverter.ToString(key).Replace("-", string.Empty);
}

You send in the bit size which must be converted to bytes, i.e. divided by 8. We generate the random bytes and convert them to a string. The BitConverter class puts a dash in between each hex value which we need to get rid of, hence the call to the Replace function.

This function can be invoked like this:

string validKey = symmDemo.GetValidEncryptionKey(128);

Importance of CBC and padding ISO10126

Run the application we’ve been working on a couple of times and you’ll see that the cipher text is always different even for the same string being encrypted. This is because we get a unique initialisation vector for each encryption and because we’re in CBC mode. Now locate the CreateCipher method and modify the padding and mode values as follows:

cipher.Padding = PaddingMode.Zeros;
cipher.Mode = CipherMode.ECB;

Run the application again with the same string to be encrypted. You should see that the cipher text is always the same. We saw in this post how it can be dangerous to show the same cipher text for string values of limited variability. Hence always use ISO10126 and CBC to add more randomness to the encryption process.

CryptoStream

You can integrate encryption and decryption with .NET streams using the CryptoStream object. Say you want to chain together different crypto transforms and save the output in a file. Let’s look at how this can be done:

public void ChainStreamOperations(string plainText)
{
	byte[] plaintextBytes = Encoding.UTF8.GetBytes(plainText);
	RijndaelManaged cipher = CreateCipher();
	using (FileStream cipherFile = new FileStream(_fileName, FileMode.Create, FileAccess.Write))
	{
		ICryptoTransform base64CryptoTransform = new ToBase64Transform();
		ICryptoTransform cipherTransform = cipher.CreateEncryptor();
		using (CryptoStream firstCryptoStream = new CryptoStream(cipherFile, base64CryptoTransform, CryptoStreamMode.Write))
		{
			using (CryptoStream secondCryptoStream = new CryptoStream(firstCryptoStream, cipherTransform, CryptoStreamMode.Write))
			{
				secondCryptoStream.Write(plaintextBytes, 0, plaintextBytes.Length);
			}
		}
	}
}

…where _fileName is a local variable such as this:

private string _fileName = @"c:\temp\result.txt";

We first create a file stream in a normal .NET way. The purpose of the base 64 crypto transform is that in this case I want to encode the result of the encryption in this way. Then we get the Rijndael crypto transform object as we saw before. You see in the using statements that we create a series of streams. The final goal is to write out the result to a file. Then we have a CryptoStream that ties together the file stream and the base 64 crypto transform. Then on top of that we run the encryption itself. So when we call secondCryptoStream.Write with the plain text it’s going to use the CryptoStream, perform the encryption, perform the base 64 encoding and finally the result is written out to the file. So it’s very straightforward to do a number of encryptions for different plain text values. The chained streams will perform the same steps on all of them.

If you are used to using streams in .NET then this might be a natural way to encrypt a string and put the result in a file.

You can view the list of posts on Security and Cryptography here.

Symmetric encryption algorithms in .NET cryptography part 1

Introduction

Symmetric encryption and decryption are probably what most people understand under “cryptography”. A symmetric algorithm is one where the encryption and decryption key is the same and is shared among the parties involved in the encryption/decryption process.

Ideally only a small group of reliable people should have access to this key. Attackers typically use brute force to find the key in an attempt to decipher an encrypted message rather than defeating the algorithm itself. The key can vary in size so the attacker will need to know this first. Once they know this then they will try combinations of possible key characters.

A clear disadvantage with this approach is that distributing and storing keys in a safe and reliable manner is difficult. On the other hand symmetric algorithms are fast.

Here’s the graphical representation of the algorithm at work:

Symmetric algorithm flow

We start with the plain text to be encrypted. The encryption algorithm runs using the common secret key. The plain text becomes cyphertext which is decrypted using the same secret key and algorithm.

A common algorithm is called AES – Advanced Encryption Standard. This has been a US government standard since 2001 when it replaced DES – Data Encryption Standard.

AES uses the so-called Rijndael algorithm with 128 bit block sizes. You can read about the details of the algorithm here. If you need to work with external partners that use disparate systems then AES is a good choice as it’s widely supported in different encryption libraries in Java, Ruby, .NET, Objective C, etc.

In .NET all symmetric algorithms derive from the SymmetricAlgorithm abstract class. AES with Rijndael is not the only implementation available, here are some others:

  • Triple DES: applies DES encryption 3 times
  • DES: used to be the standard but there were successful approaches to break the key due to its small key size of 56 bits. It is not recommended to use this algorithm in new systems – use it only if you have to support backward-compatibility or legacy systems
  • RC2: this was another competitor to replace DES

There are other symmetric algorithms out there, such as Mars, RC6, Serpent, TwoFish, but there’s no .NET implementation of them at the time of writing this post. Make sure to pick AES/Rijndael as your first choice if you need to select a symmetric algorithm in your project.

In .NET

The .NET implementations if symmetric algorithm are called block ciphers. This only means that the encryption process takes the provided plain text and breaks it up into fixed size blocks, such as 128 mentioned above. The algorithm is performed on each individual block.

It is of course difficult to guarantee that the plain text will fit into those exact block boundaries – this is where padding enters the scene. Padding is data added to fill the last block to the correct size where if it doesn’t fit the given bit size. We need to fill up this last block as the algorithm requires fix sized blocks.

Padding data can be a bunch of zeros. Another approach is called PKCS7. This one says that if there are e.g. 8 bits remaining to fill the block we’ll use that number for each one of those spots. Yet another way to fill the missing spots is called ISO10126, which fills that block with random data. This is also the recommended approach as it provides more randomness in the process which is always a good way to put extra layers of protection on your encryption mechanism.

Mode is also a factor in these algorithms. The most common one is called ECB which means that each block of the plain text will be encrypted independently of all the others. The recommended approach here is CBC – this means that a block will not only be encrypted but that a given block will be used as input to encrypt the subsequent block. So CBC adds some more randomness to the process which is always good.

In case you go with CBC then another term you’ll need to be familiar with is the IV – Initialization Vector. The IV determines what kind of random data you’re going to use for the first block simply because there’s no block before the first block to be used as input. The IV is just some random data that will be used as input in the encryption of the first block. The IV doesn’t need to be some secret string – it must be redistributed along with the cipher text to the receiver of our message. The only rule is not to reuse the IV to keep the randomness that comes with it.

Demo

Now it’s time see some code after all the theory. Create a Console application in Visual Studio. We first want to set up the Rijndael encryption mechanism with its properties. Consider the following code:

private RijndaelManaged CreateCipher()
{
	RijndaelManaged cipher = new RijndaelManaged();
	cipher.KeySize = 256;
	cipher.BlockSize = 128;
	cipher.Padding = PaddingMode.ISO10126;
	cipher.Mode = CipherMode.CBC;
	byte[] key = HexToByteArray("B374A26A71490437AA024E4FADD5B497FDFF1A8EA6FF12F6FB65AF2720B59CCF");
	cipher.Key = key;
	return cipher;
}

We instantiate a RijndaelManaged class which I guess you know what it might represent. We then set its key and block size. As mentioned above padding is set to ISO10126 and mode to CBC. The encryption key must be a valid AES key that you can find plenty of on the Internet. Note that you can set a lower key size, e.g 128, but make sure that the AES key is a valid 128 bit array in that case. Here I set the block size to 128 to be fully AES compliant, but generally the higher the key and block size the more secure the message. I’ve put the key as plain text into the code but it can be stored in the web.config file, in the database, it’s up to you. However, this key must remain secret, so storage is not a trivial issue.

The HexToByteArray method may look familiar from the previous post on hashing:

public byte[] HexToByteArray(string hexString)
{
	if (0 != (hexString.Length % 2))
	{
		throw new ApplicationException("Hex string must be multiple of 2 in length");
	}

	int byteCount = hexString.Length / 2;
	byte[] byteValues = new byte[byteCount];
	for (int i = 0; i < byteCount; i++)
	{
		byteValues[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
	}
	return byteValues;
}

In the below method we perform the actual encryption:

public void Encrypt(string plainText)
{
	RijndaelManaged rijndael = CreateCipher();
	Console.WriteLine(Convert.ToBase64String(rijndael.IV));
	ICryptoTransform cryptoTransform = rijndael.CreateEncryptor();
	byte[] plain = Encoding.UTF8.GetBytes(plainText);
	byte[] cipherText = cryptoTransform.TransformFinalBlock(plain, 0, plain.Length);
	Console.WriteLine(Convert.ToBase64String(cipherText));
}

We let the IV and the cipher text be printed on the Console window. The IV is randomly generated by .NET, you don’t need to set it yourself. We get hold of the encryptor using the CreateEncryptor method as it is implemented by the RijndaelManaged object. It implements the ICryptoTransform interface. We use this object to transform the plain text bytes to the AES-encrypted cipher text.

Run the application and inspect the IV and cipher text values. Save those values in class properties:

public string IV { get; set; }
public string CipherText { get; set; }

You can save these in the Encrypt method:

CipherText = Convert.ToBase64String(cipherText);
IV = Convert.ToBase64String(rijndael.IV);

The Decrypt method is the exact reverse of Encrypt():

public void Decrypt(string iv, string cipherText)
{
	RijndaelManaged cipher = CreateCipher();
	cipher.IV = Convert.FromBase64String(iv);
	ICryptoTransform cryptTransform = cipher.CreateDecryptor();
	byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
	byte[] plainText = cryptTransform.TransformFinalBlock(cipherTextBytes, 0, cipherTextBytes.Length);

	Console.WriteLine(Encoding.UTF8.GetString(plainText));
}

We’ll need the cipher text and the IV we saved in the Encrypt method. This time we construct a Decryptor and decrypt the cipher text using the provided key and IV. It’s important to set up the Rijndael managed object the same way as during the encryption process – key and block size, same mode etc. – otherwise the decryption will fail.

Test the entire cycle with some text, such as “Hello Crypto” and you’ll see that it’s correctly encrypted and decrypted.

You can view the list of posts on Security and Cryptography here.

Hashing algorithms and their practical usage in .NET Part 2

In the previous post we looked at hashing in general and its usage in hashing query strings. In this post we’ll look at some other areas of applicability.

Passwords

Storing passwords in clear text is obviously not a good idea. If an attacker gets hold of your user data then it should not be easy for them to retrieve user names and passwords. It’s a well-established practice to store passwords in a hashed format as they cannot be deciphered. When your user logs onto your site the provided password is hashed using the same algorithm that was employed when the same user signed up with your site. The hashed values, i.e. not the plain text passwords will be compared upon log-in. Not even you as the database owner will be able to read the password selected by the user.

However, you must still be careful to protect the hashed passwords. As we mentioned in the previous post there are not too many hashing algorithms available. So if an attacker has access to the hashed passwords then they can simply get a list of the most common passwords – ‘secret’, ‘password’, ‘passw0rd’, etc. – from the internet, iterate through the available hashing algorithms and compare them to the hashed values in your user data store. They can even write a small piece of software that loops through an online dictionary and hashes those words. This is called a “dictionary attack”. It’s only a matter of time before they find a match. Therefore it may not be a blocking point that an attacker cannot reverse a hashed password.

Hashing passwords can be done in the same way as hashing a query string which we looked at previously. Here comes a reminder showing how to hash a plain text value using the SHA1 algorithm:

byte[] textBytes = Encoding.UTF8.GetBytes("myPassword123");
SHA1 hashAlgorithm = new SHA1Managed();
byte[] hash = hashAlgorithm.ComputeHash(textBytes);
string hashedString = BitConverter.ToString(data);

Salted passwords

So you see that storing hashed values like that may not be secure enough for the purposes of your application. An extra level of security comes in the form of salted passwords which means adding some unique random data to each password. This unique data is called ‘salt’. So we take the password and the salt and hash their joint value. This increases the work required from the attacker to perform a dictionary attack against all passwords. They would need to compute salt values as well.

Again, keep in mind that if you don’t protect your user data store then the attacker will gain access to the salt values as well and again it will be a matter of time before they find a match. It may take longer of course but they can concentrate on some specific valuable accounts, like the Administrator, instead of spending time trying to find the password of a ‘normal’ user.

This is how you can generate a salt:

private static string GenerateSalt(int byteCount)
{
	RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
	byte[] salt = new byte[byteCount];
	rng.GetBytes(salt);
	return Convert.ToBase64String(salt);
}

You’ll need cryptographically strong salt values. One of the available strong random number generators in .NET is the RNGCryptoServiceProvider class. Here we’re telling it that we need a ‘byteCount’-length of random salt. The salt is then saved along with the computed hash in the database.

The extended ComputeHash method accepts this salt:

public static string ComputeHash(string password, string salt)
{
	SHA512Managed hashAlg = new SHA512Managed();
	byte[] hash = hashAlg.ComputeHash(Encoding.UTF8.GetBytes(password + salt));
	return Convert.ToBase64String(hash);
}

So when the user logs in then you must first find the user in the database for the given username. If the user exists, then you have to retrieve the salt generated at the sign-up phase. Finally take the password provided in the login form, compute the hash using the ComputeHash method which accepts the salt and compare the hashed values.

We can add an extra layer or security by providing entropy – a term we mentioned in the previous post. We can extend the ComputeHash method as follows:

public static string ComputeHash(string password, string salt, string entropy)
{
	SHA512Managed hashAlg = new SHA512Managed();
	byte[] hash = hashAlg.ComputeHash(Encoding.UTF8.GetBytes(password + salt + entropy));
	return Convert.ToBase64String(hash);
}

The entropy can be a constant that is common to all users, e.g. another cryptographically strong salt that is not stored in the user database. We can take a random value such as ‘xl1k5ss5NTE=’. The updated ComputeHash function can be called as follows:

string salt = GenerateSalt(8);
Console.WriteLine("Salt: " + salt);
string password = "secret";
string constant = "xl1k5ss5NTE=";
string hashedPassword = ComputeHash(password, salt, constant);

Console.WriteLine(hashedPassword);
Console.ReadKey();

Alternatively you can use a Keyed Hash Algorithm which we also discussed in the previous post.

Examples from .NET

The .NET framework uses hashing in a couple of places, here are some examples:

  • ViewState in ASP.NET web forms is hashed using the MAC address of the server so that an attacker cannot tamper with this value between the server and the client. This feature is turned on by default.
  • ASP.NET Membership: if you have worked with the built-in Membership features of ASP.NET then you’ll know that both the username and password are hashed and that a salt is saved along with the hashed password in the ASP.NET membership tables
  • Minification and boundling: if you don’t know what these terms mean then start here. Hashing in this case is used to differentiate between the versions of the bundled files, such as JS and CSS so that the browser ‘knows’ that there’s a new version to be collected from the server and not use the cached one

We’ll start looking at symmetric encryption in the next post.

You can view the list of posts on Security and Cryptography here.

Hashing algorithms and their practical usage in .NET Part 1

Introduction

Cryptography is an exciting topic. It has two sides: some people try to hide some information and some other people try to uncover it. The information hiding, i.e. hiding some plain text, often happens using some encryption method. Then you have hackers, security experts and the like that try to decrypt the encrypted value.

Hashing is one such encryption algorithm. It converts some string input which can vary in size into a fixed length value, also called a “digest” or a “data fingerprint”. It’s called a fingerprint because the encrypted value can represent data that is much larger in size. You can take an entire book and create a short hash value out of its entire text.

Hashing is a one-way encryption method that cannot be deciphered (reversed) making it a standard way for storing passwords and usernames.

Note that in the demos I’ll concentrate on showing functionality, I won’t care about design practices and principles – that’s beside the point and it’s up to you to create the proper abstractions and services in your own solution. E.g. static helper classes shouldn’t be the norm to create services, but they are very straightforward for demo purposes. You’ll find many recommendations in among my glob posts on how to orginise your classes into loosely coupled elements.

I’ll concentrate on various topics within cryptography in this and the next several blog posts:

  • Hashing
  • Symmetric encryption
  • Asymmetric encryption
  • Digital signatures

After that we’ll take a look at a model application that makes use of asymmetric and symmetric encryption techniques.

Hashing in .NET

Common hash algorithms derive from the HashAlgorithm abstract base class. The most important concrete implementations out of the box include:

  • MD5 which creates a 128 bit hash – this algorithm is not considered secure any more so use it only if you need to ensure backward compatibility
  • SHA – Secure Hash Algorithm which comes in different forms: SHA1 (160bit), SHA256, SHA384 and SHA512 where the numbers correspond to the bit size of the hash created – the larger the value the harder it is to compromise the hashed value.
  • KeyedHashAlgorithms (Message Authentication Code): HMACSHA and MACTripleDES

Demo: plain text hashing

Open Visual Studio and create a new Console application. Enter the following code in Main:

string plainTextOne = "Hello Crypto";
string plainTextTwo = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

SHA512Managed sha512 = new SHA512Managed();

byte[] hashedValueOfTextOne = sha512.ComputeHash(Encoding.UTF8.GetBytes(plainTextOne));
byte[] hashedValueOfTextTwo = sha512.ComputeHash(Encoding.UTF8.GetBytes(plainTextTwo));

string hexOfValueOne = BitConverter.ToString(hashedValueOfTextOne);
string hexOfValueTwo = BitConverter.ToString(hashedValueOfTextTwo);

Console.WriteLine(hexOfValueOne);
Console.WriteLine(hexOfValueTwo);
Console.ReadKey();

So we use the most secure version of the SHA family, the one that creates a 512 bit hash. We take a short and a long string to demonstrate that the hash size will be the same. The ComputeHash method will return a byte array with 64 elements, i.e. 64 bytes which is exactly 512 bits. This method accepts a byte array input hence the call to the GetBytes method. We then show a hex string representation of the byte arrays. You’ll see in the console output that this string is grouped into 2 hex characters per byte. Each pair represents the byte value of 0-255. This is typical behaviour of the BitConverter class. There are other ways though to view a byte array in a string format.

You’ll also see that the hashed value length is the same for both strings. I encourage you to change those strings, even by only a single letter, and you’ll see very different hashed values every time.

Other hash algorithms in .NET work in a similar way:

  • MD5CryptoServiceProvider
  • SHA1Managed
  • SHA256Managed
  • SHA384Managed
  • SHA512Managed
  • MACTripleDES
  • HMACMD5
  • HMACSHA256
  • HMACMD5
  • HMACRIPEMD160
  • HMACSHA512

These are all concrete classes that derive from HashAlgorithm and implement the ComputHash(byte[]) method. All of them are quite fast. Actually SHA512Managed is among the fastest among the implementations so you don’t need to worry about execution speed if you go for this very strong algorithm.

Demo: tamperproof query strings

You must have seen websites that have URLs with some long and – at first sight – meaningless query string parameters. The goal is often to construct tamperproof query strings to protect the integrity of the parameters such as a customer id. The goal is make sure that this parameter has not been modified on the client side. Note that the actual ID will still be visible in the query string, e.g. /Customers.aspx?cid=21 but we extend this URL to include a special hash: /Customers.aspx?cid=21&hash=sdfhshmfuismfsdhmf. If the client modifies either the ‘cid’ or the ‘h’ parameter then the request will be rejected.

It’s customary to use a Hash-based Message Authentication Code (HMAC), not one of the other hashing algorithms. It computes the hash of a query string when constructed on the server. When the client sends another request to the server we check that the query string was not modified by comparing it to the original hashed value. If they don’t match then we know that the client or a man-in-the-middle has tampered with the query string values and reject the request.

We’ll also use a key so that the attacker wouldn’t be able to create his own valid hash – which is why HMAC is the better choice.

Create an ASP.NET web forms app in Visual Studio. Insert a hyperlink server control somewhere on Default.aspx:

<asp:HyperLink ID="lnkAbout" runat="server">Go to about page</asp:HyperLink>

Add a new helper class called HashingHelper. Insert the following public constants:

public static readonly string _hashQuerySeparator = "&h=";
public static readonly string _hashKey = "C2CE6ACD";

The hash query separator stores the hash param identifier in the query string. The key will be used to stop an attacker from creating an own hash as mentioned above.

The following method will create the hashed query:

public static string CreateTamperProofQueryString(string basicQueryString)
{
	return string.Concat(basicQueryString, _hashQuerySeparator, ComputeHash(basicQueryString));
}

…where ComputeHash is a private method within this class:

private static string ComputeHash(string basicQueryString)
{
        byte[] textBytes = Encoding.UTF8.GetBytes(basicQueryString);
	HMACSHA1 hashAlgorithm = new HMACSHA1(Conversions.HexToByteArray(_hashKey));
	byte[] hash = hashAlgorithm.ComputeHash(textBytes);
	return Conversions.ByteArrayToHex(hash);
}

…where Conversions is a static utility class:

public static class Conversions
{
	public static byte[] HexToByteArray(string hexString)
	{
		if (0 != (hexString.Length % 2))
		{
			throw new ApplicationException("Hex string must be multiple of 2 in length");
		}

		int byteCount = hexString.Length / 2;
		byte[] byteValues = new byte[byteCount];
		for (int i = 0; i < byteCount; i++)
		{
			byteValues[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
		}

		return byteValues;
	}

	public static string ByteArrayToHex(byte[] data)
	{			
        	return BitConverter.ToString(data);
	}
}

This will look familiar from the previous demo. We’re using a keyed algorithm – HMACSHA1 – which is based on SHA1 but can accept a key in the form of a byte array. Hence the conversion to a byte array in the Conversions helper.

In the Default.aspx.cs file add the following code:

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		lnkAbout.NavigateUrl = string.Concat("/About.aspx?", HashingHelper.CreateTamperProofQueryString("cid=21&pid=43"));
	}
}

Run the project and hover over the generated link. You should see in the bottom of the web browser that the cid and pid parameters have been extended with the extra ‘h’ hash parameter.

Add a label control somewhere on the About page:

<asp:Label ID="lblQueryValue" runat="server" Text=""></asp:Label>

Put the following in the code behind:

protected override void OnLoad(EventArgs e)
{
	HashingHelper.ValidateQueryString();
	base.OnLoad(e);
}

…where ValidateQueryString looks as follows:

public static void ValidateQueryString()
{
	HttpRequest request = HttpContext.Current.Request;

	if (request.QueryString.Count == 0)
	{
		return;
	}

	string queryString = request.Url.Query.TrimStart(new char[] { '?' });

	string submittedHash = request.QueryString["h"];
	if (submittedHash == null)
	{
		throw new ApplicationException("Querystring validation hash missing!");
	}

	int hashPos = queryString.IndexOf(_hashQuerySeparator);
	queryString = queryString.Substring(0, hashPos);

	if (submittedHash != ComputeHash(queryString))
	{
		throw new ApplicationException("Querystring hash value mismatch");
	}
}

We retrieve the current HTTP request and check its query string contents. If there are no query strings then there’s nothing to validate. We then extract the entire query string bar the starting ‘?’ character from the URL. We check if any hash parameter has been sent – if not then we throw an exception. We then need to recompute the hash of the cid and pid parameters and compare that value with what’s been sent in the URL. If they don’t match then we throw an exception.

Run the application and click the link on the Home page. You’ll be directed to the About page. On the About page modify the URL in the web browser: change either query string parameter, reload the page and you should get an exception saying the hash values don’t match.

Why did we use a keyed algorithm? Let’s see first what may happen if you use a different one. In HashingHelper.ComputeHash comment out the line that creates a HMACSHA1 object and add a new line:

SHA1 hashAlgorithm = new SHA1Managed();

Run the application and you’ll see that it still works fine, the hashed value is generated and compared to the submitted value. Try changing the query parameter values and you should get the same exception as before. An attacker will look at this URL will know that this is some protected area. They will quickly figure out that this is some type of hash and they’ll start going through the different hash algorithms available out there. There aren’t that many – look at the list above with the ones built-into -.NET. It’s a matter of a couple of lines to generate hash values in a console app:

SHA1 hashAlgorithm = new SHA1Managed();
//calculate hash of cid=123&pid=32
//convert to string
//call the web site

If it doesn’t work then they’ll try a SHA256 and then SHA512 etc. It doesn’t take too long for an experienced hacker to iterate through the available implementations and eventually find the correct algorithm. They can then look at any protected value on the About page by replacing the query parameters and the hash they calculated using the little console application.

With the keyed algorithm we built in an extra blocking point for the attacker. So now the attacker will need to know the key as well as the hashing algorithm.

However, in some situations this may not be enough. If the query string parameter values are limited, such as a bit param 0 or 1 then even the keyed hash value will always only have two values: one for ‘0’ and another one for ‘1’. An attacker that’s watching the web traffic can after some time figure out what a ‘0’ hash and a ‘1’ hash looks like and they won’t need to deal with any secret keys.

Therefore we need some way to differentiate between two requests with the same parameter. We need to add some randomness to our hashing algorithm – this is called adding some “entropy”. In ASP.NET we can use the IP address of the client, their user agent, the session information and other elements that can differentiate one request from another. This is how you can add the unique session key to the data that’s being hashed:

HttpSessionState httpSession = HttpContext.Current.Session;
basicQueryString += httpSession.SessionID;
httpSession["HashIndex"] = 10;

Add this to the beginning of the HashingHelper.ComputeHash method. This will ensure that it’s not only ‘0’ or ‘1’ being hashed but some other information as well that varies a lot and is difficult to get access to. You can test it with the current setup as well. Run the application and you’ll get some hash in the query string. Stop the app, change the browser type in the Visual Studio app bar:

Change browser type in visual studio

Make sure it’s different from what you had in the previous run. Click the link on the default page and you should see that the hash value is different for the same combination of cid and pid query string values.

You can view the list of posts on Security and Cryptography here.

Claims-based authentication in .NET4.5 MVC4 with C#: External authentication with WS-Federation Part 1

Our model MVC4 internet applications in this series had one important feature in common: they all provided the authentication logic internally.

This is the traditional approach to logging in on a web page: there is a login page within the application which provides the gateway to the protected parts of the website. Upon providing the login credentials and pressing the ‘Log in’ button the web application will check the the validity of those credentials against some data store and accept or reject the request.

We will build on the demo application from the previous post on claims in MVC4. If you are new to Claims in .NET4.5 then I’d recommend that you start from the beginning.

External authentication: introduction

There are several reasons why the internal auth approach might not be the most suitable one:

  • This is not a trivial exercise: logging in and out must happen in a secure way
  • Your attention therefore may be diverted from the ‘true’ purpose of your application, i.e. the very reason for its existence
  • You may not like programming in Security-related topics which holds you back from writing the ‘real’ application logic of your app
  • Multiple authentication types are often problematic to implement: you can typically only provide one specific type of authentication on your site and it’s usually a Forms-based one
  • As the auth logic is internal to your app it is difficult to re-use in other apps that need the same type of login: the result is a copy-paste type of horror

Thus it would be nice to somehow factor out the authentication logic in a separate project/application which can perform the authentication for your web app and for any other apps that also need authentication against the same user store. The benefits of such a scenario are the following:

  • Multiple applications can share the login logic
  • Keep the authentication logic in one place and avoid the copy-paste scenario: if the logic changes it will be automatically propagated in all consuming applications, also called the RELYING PARTIES
  • It’s possible to re-use the auth session across several applications so that the user does not need to log in on multiple sites: this is called Single SignOn
  • The external apps, i.e. the relying parties can get rid of their internal auth data allowing developers to concentrate on the ‘real stuff’
  • The responsibilities are more clearly divided: the relying party carries out the business logic and the auth app takes care of the authentication
  • The relying parties can establish a trust relationship with the auth app using Federation: this is important as the external apps should not blindly accept a authentication result as it may come from an unreliable source
  • The team of developers can be divided up more efficiently: domain experts who work on the real business logic and security experts that work on the authentication and user store part
  • You can put the external auth app anywhere: on a different physical server, in the cloud, behind some web service, etc.
  • Your web app can be set up to accept claims from multiple authentication services: as long as the claims are coming from a trusted source your web app will not care which one they are coming from

What would such a scenario look like? First I’ll try to describe the scenario in words.

The external authentication app we have been talking about is called a Security Token Service, or an STS in short. It is also called an Identity Provider. The STS is a normal website with its own login page sitting on some web server.

Imagine the following:

  • You have a web page that relies on external authentication
  • Thus it will be void of all types of auth logic and it will have no Login page either
  • A client wishes to reach a protected page within your web app
  • The client will then be redirected to the LOGIN PAGE OF THE STS
  • The STS performs the authentication and issues a security token to the client upon successful login
  • This token, which we’ll talk more about later, probably does not include too many claims: user ID, user name, email
  • This token will also include an identifier that identifies the issuer of the token in a reliable way
  • The token is sent back to the client which is then redirected to the external application where the user originally wanted to log in
  • The relying party inspects the token, checks the issuer, maybe transforms the claims and can reject or accept the user depending on the validity of the token and the claims within the token
  • Example: if the issuer of the token is not coming from a trusted auth service, the signature in the token has been tampered with or an important claim is missing or is malformed then you can still reject the request in your web app very early on
  • If everything is fine with the token then the relying web app will establish a ClaimsPrincipal the same way as we saw before in related blog posts

The flow can be shown graphically as follows:

An STS model

The security token is meaningless for the client. As mentioned above, it will be used by your web app to check its validity, transform the claims etc. Also, just to stress the point, it is not important any more where the STS is located.

Security Assertion Markup Language: SAML

You may be wondering what the security token issued by the STS looks like. There are some standard and certainly lots of company-specific formats out there. The default in .NET4.5 follows the SAML format, which is sort of a specialised XML. Here comes a portion of such a token from Wikipedia:

SAML example

You’ll see the Issuer, the X509 cert data, i.e. the digital signature and the NameID in the picture. The signature will be used to see if the token has been tampered with after it left the STS and if the issuer is a trusted one. There’s typically not much else shown in a SAML token. It is up to the STS what kind of data it will include in the SAML token. The STS may provide a different set of initial claims depending on the type of application wishing to be authenticated. The good news is that you will not have to work with SAML directly; .NET will translate the XML into Claims automatically. It is also important to note that if you have complete control over the STS then it is up you what you include in the SAML: anything from UserId to EyeColour and FavouriteBand can be sent along.

WS-Federation

The protocol that makes this trust relationship and token communication possible is called WS-Federation. It is a standard and is now available in .NET4.5. The flow of communication in words is as follows:

  • The client tries to access a protected page on your Claims-enabled site by sending a HTTP GET request
  • .NET will see that the request is void of any security token so it will be redirected to the Login page of the STS by another HTTP 302 request
  • The URL of the redirect will include a special query string that may look something like this: wsfed?wa=wsignin1.0&wtrealm=[ID of relying party]
  • The query string says that we want to sign in to a certain Realm, which is the identifier of the relying party, usually its URL
  • Upon successful login the STS somehow needs to send the SAML token to the relying party, so let’s stop here for a second…

The STS will send back a form with method = “POST” which will be redirected from the client to the relying party. This form might look like the following:

<form method="post" action="address of relying party">
    <input name="wresult" value="<saml:assertion..." />
    <script>
        window.setTimeout('document.forms[0].submit()', 0);
    </script>
</form>

The STS attaches the SAML to the value attribute of the input field within the form. The form is then submitted using a very simple piece of embedded JavaScript. Let’s continue with the flow:

  • The form is POSTed back to the relying party from the client
  • The relying party will validate the token and its contents and turn it into an Identity

It’s important to stress that this is not some Microsoft specific framework targeting .NET applications only. WS-Federation is part of the larger WS* family of web service specifications. It can happen that you have an STS built with .NET and a Ruby on Rails web app that you would like to connect to the STS. The fact that the STS was implemented using .NET is an unimportant detail in the bigger picture as the communication is based on a widely accepted standard. If you are in this situation then you need to check if Ruby has built-in support for WS-Federation, which I’m pretty sure it does although I know precious little about that framework.

Security Token Service

What does an actual STS look like then? There are several commercial products out there. Examples:

.NET4.5 includes base classes that allow you to build your own STS. Beware though that this is not a trivial exercise. You must be very knowledgeable and experienced in programming Security.

There’s an open source STS available on GitHub: Thinktecture IdentityServer which we’ll take a closer look at in the next blog post.

For now you won’t need any of the real STS solutions out there while developing your solution. You can download an extension to Visual Studio which enables you to use a Development STS with pre-set claims. We will use this in the demo.

Demo

You will need to download and install the Identity and Access Tool extension from here for the demo.

This is a great tool for development purposes; you won’t need a real STS but you can still write your code that accepts the security token as if it comes from a real STS. Then when you’re done you simply replace the tool with the STS of your choice.

Open the MVC4 application from the previous post. As it currently stands this application still uses Forms-based authentication and we’ll try to convert it to a Claims-based one.

Before we change anything let’s note some important identity-related aspects of web.config:

1. We have our system.identityModel section where we registered the custom authentication and custom authorisation managers:

<system.identityModel>
    <identityConfiguration>
      <claimsAuthenticationManager type="ClaimsInMvc4.CustomClaimsTransformer,ClaimsInMvc4" />
      <claimsAuthorizationManager type="ClaimsInMvc4.CustomAuthorisationManager,ClaimsInMvc4" />
    </identityConfiguration>
  </system.identityModel>

2. We let users log in by their usernames and passwords on our login page:

<authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>

3. We registered a session authentication module under the modules node:

<modules>
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"></add>
</modules>

4. There is no authorization element, meaning we let anonymous users view the unprotected pages of the website.

Upon successful installation of the Identity and Access Tool you should see a new menu point when you right-click the project:

Identity tool menu point

Click on the underlined menu point which will open up the Identity and Access window:

Identity and access window

You have here a number of options to add an STS to your project:

  • Local Development STS is the one you’ll want to use for development purposes if you don’t have a real STS available
  • A business identity provider, like the ones listed above, e.g. Oracle
  • An Azure cloud based STS

Select the first option. You can then select the ‘Local Development STS’ tab:

Local development STS tab

You will see a list of test claims that the web application will receive, such as the name ‘Terry’. Again, keep in mind that there’s no way to directly log on to a fully claims-based web app; here we pretend that an external STS is sending these claims to your application after a user has successfully signed in on the login page of the STS. You can configure this list according to the needs of your token validation and authorisation logic.

Change the value of the name claim, i.e. the very first one to the name of the user you created in the previous blog posts, so I’ve changed mine to ‘Andras’.

You can select the SAML version: either 1.1 or 2.0. This depends on the available versions of the STS of your choice. In our case it doesn’t make any difference, so leave option 1.1 selected.

Click OK and let’s see what happens. At first you won’t see any changes. Let’s inspect web.config though:

1. The system.identityModel has been extended to include claims-related elements:

<system.identityModel>
    <identityConfiguration>
      <claimsAuthenticationManager type="ClaimsMvc.CustomClaimsTransformer,ClaimsMvc" />
      <claimsAuthorizationManager type="ClaimsMvc.CustomAuthorisationManager,ClaimsMvc" />
      <audienceUris>
        <add value="http://localhost:2533/" />
      </audienceUris>
      <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
        <authority name="LocalSTS">
          <keys>
            <add thumbprint="9B74CB2F320F7AAFC156E1252270B1DC01EF40D0" />
          </keys>
          <validIssuers>
            <add name="LocalSTS" />
          </validIssuers>
        </authority>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
    </identityConfiguration>
  </system.identityModel>

We will discuss these elements in more detail in the next blog post. Note the following: the Identity and Access Tool is periodically updated and can be downloaded from within Visual Studio. Select Extensions and Updates… in the Tools menu. Make sure you check if there are any updates available under the Updates menu point:

Tools updates in Visual Studio

When I published the first version of this post – some time in March 2013 – the above XML was slightly different. I updated the Identity and Access Tool on 12 May 2013 which yielded the above system.identityModel node. It is possible that when you read this post the Access Tool will again yield something different. Let me know in the comments section if you notice a change and I’ll update this post accordingly.

2. Forms-based login is gone:

<authentication mode="None" />

3. The modules element has been extended with WS-Federation:

<modules>
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      </add>
      <remove name="FormsAuthentication" />
      <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
    </modules>

4. By default we’ll deny access to anonymous users:

<authorization>
      <deny users="?" />
    </authorization>

Run the application and you may be greeted with the following error message:

Must have admin rights to local STS

If you started VS in admin mode then you shouldn’t see this, I’ll just restart mine.

Watch the browser bar carefully while the page is loading. At some point there should be a URL similar to this:

http://localhost:12175/wsFederationSTS/Issue/?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a2533%2f&wctx=rm%3d0%26id%3dpassive%26ru%3d%252f&wct=2013-05-12T12%3a22%3a58Z

This is the external ‘login page’, but there’s of course no external login page of the model STS. This is what’s happening:

  • Web.config has been changed by the identity tool to deny access to all anonymous users
  • When you run the application you will initially be an anonymous user
  • Your request is redirected to the model STS page on localhost:12175. Remember that this was the port number that we selected in the Identity and Access window. Don’t worry if yours has a different port number, it doesn’t make any difference
  • You will probably recognise the format of the URL with ‘?wa=wsignin1.0&wtrealm=’ followed by the URL of the MVC4 website
  • The local STS returns the list of claims we specified in the Identity and Access window
  • The request is redirected to our web page and the user is logged in
  • The request is redirected by the forms-based mechanism we discussed above where the form containing the SAML value of the authentication token was submitted by JavaScript

Recall that we protected the About page with the ClaimsAuthorize attribute:

[ClaimsAuthorize("Show", "Code")]
        public ActionResult About()

…which will activate our custom authorisation logic in CustomAuthorisationManager.cs:

public class CustomAuthorisationManager : ClaimsAuthorizationManager
    {
        public override bool CheckAccess(AuthorizationContext context)
        {
            string resource = context.Resource.First().Value;
            string action = context.Action.First().Value;

            if (action == "Show" && resource == "Code")
            {
                bool livesInSweden = context.Principal.HasClaim(ClaimTypes.Country, "Sweden");
                bool isAndras = context.Principal.HasClaim(ClaimTypes.GivenName, "Andras");
                return isAndras && livesInSweden;
            }

            return false;
        }
    }

Add two breakpoints to the application: one within CustomClaimsTransformer.Authenticate and one within CustomAuthorisationManager.CheckAccess. Re-run the application. If the code execution hasn’t stopped then click the Log off link to force a new ‘login’ via the local STS. Code execution should stop at CustomClaimsTransformer.Authenticate. This is good news as our custom auth manager still kicks in and dresses up the Principal with our custom claims…:

private ClaimsPrincipal DressUpPrincipal(String userName)
        {
            List<Claim> claims = new List<Claim>();

            //simulate database lookup
            if (userName.IndexOf("andras", StringComparison.InvariantCultureIgnoreCase) > -1)
            {
                claims.Add(new Claim(ClaimTypes.Country, "Sweden"));
                claims.Add(new Claim(ClaimTypes.GivenName, "Andras"));
                claims.Add(new Claim(ClaimTypes.Name, "Andras"));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, "Andras"));
                claims.Add(new Claim(ClaimTypes.Role, "IT"));
            }
            else
            {
                claims.Add(new Claim(ClaimTypes.GivenName, userName));
                claims.Add(new Claim(ClaimTypes.Name, userName));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, userName));
            }

            return new ClaimsPrincipal(new ClaimsIdentity(claims, "Custom"));
        }

…and also establishes the authentication session as per the CreateSession method. Now click the About link on the front page. As this is a protected page code execution will stop within CustomAuthorisationManager.CheckAccess which shows that even this custom manager class works as it should. Upon successful authorisation the About page should load as expected.

So our previous investments are still worth the effort. The external login doesn’t invalidate our claims authentication and claims transformation logic.

In the next post we’ll look at the changes in web.config in more details and hook up our MVC4 with a real STS.

You can view the list of posts on Security and Cryptography here.

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

ARCHIVED: Bite-size insight on Cyber Security for the not too technical.