2 // PrivateKey.cs - Private Key (PVK) Format Implementation
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
13 using System.Security.Cryptography;
16 using Mono.Security.Cryptography;
18 namespace Mono.Security.Authenticode {
21 // a. http://www.drh-consultancy.demon.co.uk/pvk.html
23 public class PrivateKey {
25 private const uint magic = 0xb0b5f11e;
27 private bool encrypted;
34 keyType = 2; // required for MS makecert !!!
37 public PrivateKey (byte[] data, string password)
39 Decode (data, password);
42 public bool Encrypted {
43 get { return encrypted; }
47 get { return keyType; }
48 set { keyType = value; }
57 get { return ((encrypted) ? weak : true); }
61 private byte[] DeriveKey (byte[] salt, string password)
63 byte[] pwd = Encoding.ASCII.GetBytes (password);
64 SHA1 sha1 = (SHA1)SHA1.Create ();
65 sha1.TransformBlock (salt, 0, salt.Length, salt, 0);
66 sha1.TransformFinalBlock (pwd, 0, pwd.Length);
67 byte[] key = new byte [16];
68 Buffer.BlockCopy (sha1.Hash, 0, key, 0, 16);
70 Array.Clear (pwd, 0, pwd.Length);
74 private bool Decode (byte[] pvk, string password)
77 if (BitConverter.ToUInt32 (pvk, 0) != magic)
80 if (BitConverter.ToUInt32 (pvk, 4) != 0x0)
83 keyType = BitConverter.ToInt32 (pvk, 8);
85 encrypted = (BitConverter.ToUInt32 (pvk, 12) == 1);
87 int saltlen = BitConverter.ToInt32 (pvk, 16);
89 int keylen = BitConverter.ToInt32 (pvk, 20);
90 byte[] keypair = new byte [keylen];
91 Buffer.BlockCopy (pvk, 24 + saltlen, keypair, 0, keylen);
92 // read salt (if present)
97 byte[] salt = new byte [saltlen];
98 Buffer.BlockCopy (pvk, 24, salt, 0, saltlen);
99 // first try with full (128) bits
100 byte[] key = DeriveKey (salt, password);
101 // decrypt in place and try this
102 RC4 rc4 = RC4.Create ();
103 ICryptoTransform dec = rc4.CreateDecryptor (key, null);
104 int x = 24 + salt.Length + 8;
105 dec.TransformBlock (keypair, 8, keypair.Length - 8, keypair, 8);
107 rsa = CryptoConvert.FromCapiPrivateKeyBlob (keypair);
110 catch (CryptographicException) {
112 // second change using weak crypto
113 Buffer.BlockCopy (pvk, 24 + saltlen, keypair, 0, keylen);
114 // truncate the key to 40 bits
115 Array.Clear (key, 5, 11);
117 RC4 rc4b = RC4.Create ();
118 dec = rc4b.CreateDecryptor (key, null);
119 dec.TransformBlock (keypair, 8, keypair.Length - 8, keypair, 8);
120 rsa = CryptoConvert.FromCapiPrivateKeyBlob (keypair);
122 Array.Clear (key, 0, key.Length);
126 // read unencrypted keypair
127 rsa = CryptoConvert.FromCapiPrivateKeyBlob (keypair);
128 Array.Clear (keypair, 0, keypair.Length);
131 // zeroize pvk (which could contain the unencrypted private key)
132 Array.Clear (pvk, 0, pvk.Length);
134 return (rsa != null);
137 public void Save (string filename)
139 Save (filename, null);
142 public void Save (string filename, string password)
145 FileStream fs = File.Open (filename, FileMode.Create, FileAccess.Write);
148 byte[] empty = new byte [4];
149 byte[] data = BitConverter.GetBytes (magic);
150 fs.Write (data, 0, 4); // magic
151 fs.Write (empty, 0, 4); // reserved
152 data = BitConverter.GetBytes (keyType);
153 fs.Write (data, 0, 4); // key type
155 encrypted = (password != null);
156 blob = CryptoConvert.ToCapiPrivateKeyBlob (rsa);
158 data = BitConverter.GetBytes (1);
159 fs.Write (data, 0, 4); // encrypted
160 data = BitConverter.GetBytes (16);
161 fs.Write (data, 0, 4); // saltlen
162 data = BitConverter.GetBytes (blob.Length);
163 fs.Write (data, 0, 4); // keylen
165 byte[] salt = new byte [16];
166 RC4 rc4 = RC4.Create ();
169 // generate new salt (16 bytes)
170 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
172 fs.Write (salt, 0, salt.Length);
173 key = DeriveKey (salt, password);
175 Array.Clear (key, 5, 11);
176 ICryptoTransform enc = rc4.CreateEncryptor (key, null);
177 // we don't encrypt the header part of the BLOB
178 enc.TransformBlock (blob, 8, blob.Length - 8, blob, 8);
181 Array.Clear (salt, 0, salt.Length);
182 Array.Clear (key, 0, key.Length);
187 fs.Write (empty, 0, 4); // encrypted
188 fs.Write (empty, 0, 4); // saltlen
189 data = BitConverter.GetBytes (blob.Length);
190 fs.Write (data, 0, 4); // keylen
193 fs.Write (blob, 0, blob.Length);
196 // BLOB may include an uncrypted keypair
197 Array.Clear (blob, 0, blob.Length);
202 static public PrivateKey CreateFromFile (string filename)
204 return CreateFromFile (filename, null);
207 static public PrivateKey CreateFromFile (string filename, string password)
209 FileStream fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
210 byte[] pvk = new byte [fs.Length];
211 fs.Read (pvk, 0, pvk.Length);
214 return new PrivateKey (pvk, password);