Jump to navigation

Poupou's Corner of the Web

Looking for perfect security? Try a wireless brick.
Otherwise you may find some unperfect stuff here...


The new (v2) System.Security.Cryptography.Pkcs

Long time no blog but I'm still alive and still looking into the new framework. Most new stuff in mscorlib's System.Security.Cryptography had already been commited in CVS. A big thanks to Pieter Philippaerts for his RIPEMD160 hash implementation.

Lately I've been working, on and off, on PKCS #7 Cryptographic Message Syntax Standard. PKCS #7 are envelopes for signing and encrypting data - something you could compare to XML Digital Signature and XML Encryption but ASN.1 based (which is much less popular these days ;-). The best known use of PKCS#7 is probably the S/MIME specification for digitally signing and encrypting emails using X.509 certificates.

The 1.2 framework includes support for signed and encrypted PKCS#7 envelopes in the System.Security assembly. Mono.Security assembly already includes support for PKCS #7 signed envelopes for decoding Authenticode® signatures and Software Publisher Certificates (SPC) files. So far it has been easy to implement the new API using Mono.Security as it's foundation - i.e. the Mono API is a much lower level API (which is both good and bad depending on your ASN.1 knowledge). The encryption stuff is new, as Mono didn't require it before, but shouldn't be too complex to implement as it share much code with the existing stuff.

What's taking me longer is that I need to implement the new X509CertificateEx and all the new classes in System.Security.Cryptography.X509Certificates (in System.Security assembly) which requires yet another PKCS standard, PKCS #12 Personal Information Exchange Syntax Standard, which in turn requires PKCS #8 Private-Key Information Syntax Standard (commited to CVS a few days ago).

The good news is that I can decode all (well the ones I got) PKCS#12 containing RSA keypairs :-) but that's the subject for another post...

While being on the PKCS7 subject I did an interesting lecture on some PDC slides on MSDN. Some screenshots were included to illustrate an example and you could clearly see that they were using framework version 2.0.3500.0, not the version 1.2.3400.0 they handed out to attendees. This same presentation also showed some classes in System.Security.Cryptography.Pkcs, namely EnvelopedCms and SignedCms. Those aren't in the 1.2 beta. EnvelopedPkcs7 and SignedPkcs7 are part of 1.2 but their documentation clearly indicates that they do not support CMS.

I will definitively continue reading the stuff :)

11/29/2003 14:19:06 | Comments | Permalink

Stronger results !

Today, when I was coming back from work and looking sadly at the snow, I got a flash - the kind that hurt my feelings but it's still better than being hit by a snowball.

I was thinking about my answer to Miguel's email about strongnaming the Mono's assemblies. I realized that I forgot to talk to him about the special ECMA key that we would need to support before depending on the strongnames. That's where the flash hit me - that damn ECMA key was, most probably, the source of most of my bad strongname verifications!

A quick change to my scangac.exe tool was able to add a new category for assembly using the ECMA key - 00000000000000000400000000000000.

I also took the time to fix the exception that nine assemblies had within my code. Real life .NET assemblies MZ headers aren't all 128 bytes long - whatever the specifications may says.

So the latest results are:

> scangac c:\windows\assembly

Good: 153
ECMA: 38
Bad: 24
Exception: 3

and are exactly like Microsoft's sn would report it :-). I won't claim perfect results - but it's sure a lot better than it looked yesterday!

11/5/2003 23:06:05 | Comments | Permalink

Strong results ?

I must admit that didn't do very exhaustive testing on the strongname stuff - maybe because it didn't seem fun enough. Then I got the idea to write a tool to check all assemblies in my GAC - which has two versions of the framework installed. That looked more fun :-) so here are my results for running a strongname verification on my computer's GAC.

> scangac c:\windows\assembly

Good: 144
Bad: 62
Exception: 12

So for a total of 218 assemblies I get 66 % good, 28% bad and exceptions for the last 6 %. This was disappointing :-(. Normally I try to hit at least 80% before releasing something like this.

I started looking at the assemblies that produced exceptions - it seemed easier as there was only 12 of them ;-). 9 out of the 12 are NullReferenceException in my code, while the last 3 comes from the MS framework when I tried to load the AssemblyName.

  • System.EnterpriseServices.Thunk.dll
  • System.EnterpriseServices.Wrapper.dll (2 times at different locations)

I tried to run Microsoft's sn tools on the three assemblies and was told, each time, that the "assembly does not represent a strongly named assembly". Seems I can add 3 to my conformance total :-).

Next came the bad assemblies. I wasn't about to run 62 times sn, again not fun, so I modified my tool to generate a script using all bad assemblies. The resulting log was surprising... there was 42 bad (as in non strongnamed) assemblies in my GAC - all of them NativeImages! That's something I need to investigate further... but this also means that we give the same results for 42 more assembles :-).

So the official statistics for comparison should be:

Good: 167
Bad: 42
Exception: 9

Meaning that we're off by 23 assemblies - just under 11%. Still got work todo, but much better to my ego :-)

11/4/2003 20:42:53 | Comments | Permalink

Common question: How to encrypt using RSA

This is very popular question in the newsgroups. Many people answers it without a full comprehension of the (non) problem. So it seems to stay a mysterious subject for many people.

Problem: People try to encrypt using RSA and face a limit of 117 bytes.

Why ? Here's a simple explaination...

RSA is all about mathematics - it's a simple equation with big numbers (as opposed to a complex equation with small numbers ;-). What you are encrypting is only a value in the equation. Because, by default the framework, RSA uses 1024 bits keypair your value must have 128 bytes (1024 bits / 8 bits/byte).

Next comes the padding. Raw encryption, without padding, is not secure because your (128 bytes) number could be very small. Raw encryption isn't possible using the Microsoft .NET framework - EncryptValue and DecryptValue thrown an UnsupportedException (but is supported with Mono::). This is a restriction of CryptoAPI used in RSACryptoServiceProvider.

The most common padding mechanism (for asymmetric keys) is PKCS#1 version 1.5. Without describing the padding, look at PKCS#1 specification or Mono's source code if you want the details, let's say that the PKCS#1 padding length is 11 bytes. So the theoratical maximum of 128 bytes minus the 11 bytes of padding gives you a maximum of 117 bytes for encryption.

A newer padding mechanism is OAEP. It's not much used in standards right now but the padding algorithm has been proven secure (warning: this may not means to you the same thing that it does to a cryptographer ;-). CryptoAPI supports OAEP only for Windows XP and later operating systems - the documentation talks about Windows 2000 with the high encryption pack but I was unable to make it work. As Mono doesn't use CryptoAPI it doesn't have this restriction, so OAEP is available anywhere Mono works :-).

Next question: Could someone iterate the encryption process for every 117 bytes (PKCS#1) ? The anwser is yes but that would be a very slow process - and I wouldn't recommand it as there is a much better alternative.

Now the implied question - which is, much of the time, unanwsered on the newsgroups: Knowing the limitation about encrypting with RSA, what should I do ?

A key exchange. Why ?

First, RSA is slow and symmetric algorithms, like AES, are fast. So encrypting all the data using a symmetric algorithm make sense - performance wise. It also make sense "security wise" because a 128 bits symmetric key is more secure than a 1024 bits asymmetric keypair (at least for RSA) - so we aren't weakening the total security of the encryption.

Second, symmetric key size are much smaller (e.g. AES 256 bits == 32 bytes) than the 117 bytes limits of PKCS#1. So it's easy to encrypt a symmetric key using the RSA public key.

Third, symmetric key management is complex so we don't want to do that. Using password to derive keys is possible (but that's another often misundertood subject) but less secure. Anyway we already have a solution because we can concatenate the results of 1 (encrypted data) and 2 (encrypted key).

This means that the receiver must first decrypt the symmetric key using it's private key. Once he have the symmetric key he can decrypt the whole document. This is much faster than using only RSA, less complex than handling secret keys between many people, and more secure than using password to derive keys. PKCS#7 describes a, ASN.1 based, data format that support encryption (and signature). The new v2 framework will include a namespace, System.Security.Cryptography.Pkcs to handle such structures.

I like to think that I'm better at C# than in english so here a little code to illustrate the concept:

static byte[] Encrypt (RSA rsa, byte[] input) { // by default this will create a 128 bits AES (Rijndael) object SymmetricAlgorithm sa = SymmetricAlgorithm.Create (); ICryptoTransform ct = sa.CreateEncryptor (); byte[] encrypt = ct.TransformFinalBlock (input, 0, input.Length); RSAPKCS1KeyExchangeFormatter fmt = new RSAPKCS1KeyExchangeFormatter (rsa); byte[] keyex = fmt.CreateKeyExchange (sa.Key); // return the key exchange, the IV (public) and encrypted data byte[] result = new byte [keyex.Length + sa.IV.Length + encrypt.Length]; Buffer.BlockCopy (keyex, 0, result, 0, keyex.Length); Buffer.BlockCopy (sa.IV, 0, result, keyex.Length, sa.IV.Length); Buffer.BlockCopy (encrypt, 0, result, keyex.Length + sa.IV.Length, encrypt.Length); return result; } static byte[] Decrypt (RSA rsa, byte[] input) { // by default this will create a 128 bits AES (Rijndael) object SymmetricAlgorithm sa = SymmetricAlgorithm.Create (); byte[] keyex = new byte [rsa.KeySize >> 3]; Buffer.BlockCopy (input, 0, keyex, 0, keyex.Length); RSAPKCS1KeyExchangeDeformatter def = new RSAPKCS1KeyExchangeDeformatter (rsa); byte[] key = def.DecryptKeyExchange (keyex); byte[] iv = new byte [sa.IV.Length]; Buffer.BlockCopy (input, keyex.Length, iv, 0, iv.Length); ICryptoTransform ct = sa.CreateDecryptor (key, iv); byte[] decrypt = ct.TransformFinalBlock (input, keyex.Length + iv.Length, input.Length - (keyex.Length + iv.Length)); return decrypt; }

I also wrote a little and very simple tool using the same source code (i.e. it's not meant for production, or even personnal, use). Here is how to use the tool:

crypt /k "keypair"
Create a new keypair. The file "keypair".key contains both the private and public keys and you should it really private (it's not encrypted). The file "keypair".pub only contains the public key and can be shared with your friends.
crypt /e "file" "keypair"
This will encrypt the file "file" using the public key contained in the file "keypair".
crypt /d "file" "keypair"
This will decrypt the file "file" using the private key contained in the file "keypair".

Here's how to test the tool:

> crypt > crypt.txt

> crypt /k poupou
CRYPT sample CRYPT sample for educational use only
Copyright 2003 Motus Technologies. All rights reserved. Released under MIT X.11

Created private file poupou.key
Created public file poupou.pub

> crypt /e crypt.txt poupou

> crypt /d crypt.txt.enc poupou

You could use this tool to exchange documents with friends - assuming you have their public key. (Oh no, not again) But how would they send their public key to you ? by email ? how would you trust that this is really their public key or even their email ? hmmm... you had better sent a wireless brick - if you had one ;-).

Note: Yeah yeah I know this is exactly what we do with Mono's CVS using SSH.

11/1/2003 13:51:19 | Comments | Permalink

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.