One of the most difficult problems in cryptographic key management is keeping a secret key safe from both compromise and loss. If you don't make enough backups, the key might be destroyed in a hardware failure or natural disaster. But if any backup is compromised, the key is compromised.

Rather than invent new tools, one might try to solve the problem with encryption. Just encrypting the key normally is not helpful, since then you have the same problem with the key needed to decrypt it. Instead, if you encrypt your key using multiple keys, then store these keys in different locations, you need all the keys to decrypt it. This protects against compromise, but if any key is lost, so is the original key. To fix this, you'd have to encrypt the original key many times, each time using just some of the keys. For example, suppose you had 5 keys and you wanted to be able to decrypt using any 3 of them. Then you'd need to perform all the following encryptions:
  • E1(E2(E3(K)))
  • E1(E2(E4(K)))
  • E1(E2(E5(K)))
  • E1(E3(E4(K)))
  • E1(E3(E5(K)))
  • E1(E4(E5(K)))
  • E2(E3(E4(K)))
  • E2(E3(E5(K)))
  • E2(E4(E5(K)))
  • E3(E4(E5(K)))
The number of encryptions needed grows very quickly. It also takes quite a bit of time and space to store all these multiply-encrypted keys. Let's look at some other solutions.

One solution is to simply break the key file into two parts, make a copy of each, and store the four parts in four locations. Then, even if any one is compromised, the key is not revealed. Unfortunately, this scheme is not that helpful, because just knowing half of someone's private key is enough to drastically decrease the time needed for a brute-force search to crack the remainder of the key.

Here's another simple solution: create a one-time pad (a file of random data) the same size as the private key, then XOR them together. Put the result in one place and the pad in another, and destroy the original key. We call each file a "share" or "shadow". Later, you can XOR the two shares together to retrieve the original key. Since the bytes of each file are random, you can be assured that neither piece will reveal any information about the key by itself. We can extend this scheme to n pieces by just creating n - 1 one-time pads, adding them together, and subtracting this from the key data to obtain the nth share. Unfortunately, if even one piece is lost, so is the key. This is called an (n,n) secret sharing scheme, because we have n shares and need n of them to reconstruct the secret.

Here's another more useful scheme: let the private key be represented as the number S. Choose a random line in the plane passing through the point (0, S). Next, choose n random points on that line as your n shares. Now, if we know any two of the shares, we know the line, and so can find S. If we only know one share, then we just know one point, and for every T there is a line passing through (0, T) and that point; hence, the secret could be anything at all. This is called an (n,2) secret sharing scheme, because we have n shares and need 2 of them to reconstruct the secret.

Particularly useful for individuals is the (3,2) scheme - you can keep one share on your person on a floppy or USB pen drive at all times, one share on your hard drive, and one share backed up at a secure remote location. When you need to use the key, you recreate it briefly using the shares on your hard drive and your person, then destroy it. If you lose the floppy or your hard disk crashes, you can recreate the key from the remaining shares (and make yourself 3 new shares). If any one share is compromised, the key is not compromised. If you know of a compromise, you can recreate the key, create 3 new shares, and destroy the old ones, preventing the attacker from getting a second share. The new shares are unrelated to the old ones because a different random line through (0,S) is used.

In 1979, Shamir generalized these ideas even further to create a general (n,k) secret sharing scheme for any n, k. The idea is to choose a random degree-k polynomial passing through (0,S), where S is the secret. Then choose n random points on this curve. Because there is exactly one degree-k polynomial passing through any given set of k points, any k of these points define the curve and allow us to retrieve the secret. If we have only k - 1 points, then we don't learn anything about S, because there is a polynomial passing through (0,T) and those k - 1 points for every T. To make the notion of choosing random polynomials and random points precise, Shamir works with polynomials modulo a prime number p, choosing polynomial coefficients and point coordinates randomly from the integers in [0, p - 1].

Secret sharing has more applications than just protecting private keys. It's also useful in situations where we want to entrust a secret to a group of people but want to ensure that several of them must cooperate to access the secret. We don't need to trust them all individually, nor do they all need to cooperate, so the secret is still available if members go missing or refuse to cooperate.

Finally, I wouldn't just tell you all this without providing some working code you can use. You can download an excellent implementation of the scheme for UNIX called secsplit (direct download). I don't know of any free .NET implementation, but I'm sure that porting this 600-line program into any environment wouldn't be difficult.

Some more resources on secret sharing: