2 // PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard
3 // ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
9 // (C) 2004 Novell (http://www.novell.com)
13 using System.Collections;
14 using System.Security.Cryptography;
17 using Mono.Security.Cryptography;
18 using Mono.Security.X509;
20 namespace Mono.Security.Cryptography {
22 public sealed class PKCS8 {
34 static public KeyInfo GetType (byte[] data)
37 throw new ArgumentNullException ("data");
39 KeyInfo ki = KeyInfo.Unknown;
41 ASN1 top = new ASN1 (data);
42 if ((top.Tag == 0x30) && (top.Count > 0)) {
43 ASN1 firstLevel = top [0];
44 switch (firstLevel.Tag) {
46 ki = KeyInfo.PrivateKey;
49 ki = KeyInfo.EncryptedPrivateKey;
55 throw new CryptographicException ("invalid ASN.1 data");
61 * PrivateKeyInfo ::= SEQUENCE {
63 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
64 * privateKey PrivateKey,
65 * attributes [0] IMPLICIT Attributes OPTIONAL
70 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
72 * PrivateKey ::= OCTET STRING
74 * Attributes ::= SET OF Attribute
76 public class PrivateKeyInfo {
79 private string _algorithm;
81 private ArrayList _list;
83 public PrivateKeyInfo ()
86 _list = new ArrayList ();
89 public PrivateKeyInfo (byte[] data) : this ()
96 public string Algorithm {
97 get { return _algorithm; }
98 set { _algorithm = value; }
101 public ArrayList Attributes {
102 get { return _list; }
105 public byte[] PrivateKey {
109 return (byte[]) _key.Clone ();
113 throw new ArgumentNullException ("PrivateKey");
114 _key = (byte[]) value.Clone ();
119 get { return _version; }
122 throw new ArgumentOutOfRangeException ("negative version");
129 private void Decode (byte[] data)
131 ASN1 privateKeyInfo = new ASN1 (data);
132 if (privateKeyInfo.Tag != 0x30)
133 throw new CryptographicException ("invalid PrivateKeyInfo");
135 ASN1 version = privateKeyInfo [0];
136 if (version.Tag != 0x02)
137 throw new CryptographicException ("invalid version");
138 _version = version.Value [0];
140 ASN1 privateKeyAlgorithm = privateKeyInfo [1];
141 if (privateKeyAlgorithm.Tag != 0x30)
142 throw new CryptographicException ("invalid algorithm");
144 ASN1 algorithm = privateKeyAlgorithm [0];
145 if (algorithm.Tag != 0x06)
146 throw new CryptographicException ("missing algorithm OID");
147 _algorithm = ASN1Convert.ToOid (algorithm);
149 ASN1 privateKey = privateKeyInfo [2];
150 _key = privateKey.Value;
152 // attributes [0] IMPLICIT Attributes OPTIONAL
153 if (privateKeyInfo.Count > 3) {
154 ASN1 attributes = privateKeyInfo [3];
155 for (int i=0; i < attributes.Count; i++) {
156 _list.Add (attributes [i]);
161 public byte[] GetBytes ()
163 ASN1 privateKeyAlgorithm = new ASN1 (0x30);
164 privateKeyAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
165 privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL
167 ASN1 pki = new ASN1 (0x30);
168 pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version }));
169 pki.Add (privateKeyAlgorithm);
170 pki.Add (new ASN1 (0x04, _key));
172 if (_list.Count > 0) {
173 ASN1 attributes = new ASN1 (0xA0);
174 foreach (ASN1 attribute in _list) {
175 attributes.Add (attribute);
177 pki.Add (attributes);
180 return pki.GetBytes ();
185 static private byte[] RemoveLeadingZero (byte[] bigInt)
188 int length = bigInt.Length;
189 if (bigInt [0] == 0x00) {
193 byte[] bi = new byte [length];
194 Buffer.BlockCopy (bigInt, start, bi, 0, length);
198 static private byte[] Normalize (byte[] bigInt, int length)
200 if (bigInt.Length == length)
202 else if (bigInt.Length > length)
203 return RemoveLeadingZero (bigInt);
206 byte[] bi = new byte [length];
207 Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
213 * RSAPrivateKey ::= SEQUENCE {
215 * modulus INTEGER, -- n
216 * publicExponent INTEGER, -- e
217 * privateExponent INTEGER, -- d
218 * prime1 INTEGER, -- p
219 * prime2 INTEGER, -- q
220 * exponent1 INTEGER, -- d mod (p-1)
221 * exponent2 INTEGER, -- d mod (q-1)
222 * coefficient INTEGER, -- (inverse of q) mod p
223 * otherPrimeInfos OtherPrimeInfos OPTIONAL
226 static public RSA DecodeRSA (byte[] keypair)
228 ASN1 privateKey = new ASN1 (keypair);
229 if (privateKey.Tag != 0x30)
230 throw new CryptographicException ("invalid private key format");
232 ASN1 version = privateKey [0];
233 if (version.Tag != 0x02)
234 throw new CryptographicException ("missing version");
236 if (privateKey.Count < 9)
237 throw new CryptographicException ("not enough key parameters");
239 RSAParameters param = new RSAParameters ();
240 // note: MUST remove leading 0 - else MS wont import the key
241 param.Modulus = RemoveLeadingZero (privateKey [1].Value);
242 int keysize = param.Modulus.Length;
243 int keysize2 = (keysize >> 1); // half-size
244 // size must be normalized - else MS wont import the key
245 param.D = Normalize (privateKey [3].Value, keysize);
246 param.DP = Normalize (privateKey [6].Value, keysize2);
247 param.DQ = Normalize (privateKey [7].Value, keysize2);
248 param.Exponent = RemoveLeadingZero (privateKey [2].Value);
249 param.InverseQ = Normalize (privateKey [8].Value, keysize2);
250 param.P = Normalize (privateKey [4].Value, keysize2);
251 param.Q = Normalize (privateKey [5].Value, keysize2);
253 RSA rsa = RSA.Create ();
254 rsa.ImportParameters (param);
259 * RSAPrivateKey ::= SEQUENCE {
261 * modulus INTEGER, -- n
262 * publicExponent INTEGER, -- e
263 * privateExponent INTEGER, -- d
264 * prime1 INTEGER, -- p
265 * prime2 INTEGER, -- q
266 * exponent1 INTEGER, -- d mod (p-1)
267 * exponent2 INTEGER, -- d mod (q-1)
268 * coefficient INTEGER, -- (inverse of q) mod p
269 * otherPrimeInfos OtherPrimeInfos OPTIONAL
272 static public byte[] Encode (RSA rsa)
274 RSAParameters param = rsa.ExportParameters (true);
276 ASN1 rsaPrivateKey = new ASN1 (0x30);
277 rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 }));
278 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus));
279 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent));
280 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D));
281 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P));
282 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q));
283 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP));
284 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ));
285 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ));
287 return rsaPrivateKey.GetBytes ();
290 // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
291 // which isn't enough for rebuilding the keypair. The other parameters
292 // can be found (98% of the time) in the X.509 certificate associated
293 // with the private key or (2% of the time) the parameters are in it's
294 // issuer X.509 certificate (not supported in the .NET framework).
295 static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters)
297 ASN1 pvk = new ASN1 (privateKey);
299 throw new CryptographicException ("invalid private key format");
301 // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
302 dsaParameters.X = Normalize (privateKey, 20);
303 DSA dsa = DSA.Create ();
304 dsa.ImportParameters (dsaParameters);
308 static public byte[] Encode (DSA dsa)
310 DSAParameters param = dsa.ExportParameters (true);
311 return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes ();
314 static public byte[] Encode (AsymmetricAlgorithm aa)
317 return Encode ((RSA)aa);
319 return Encode ((DSA)aa);
321 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
326 * EncryptedPrivateKeyInfo ::= SEQUENCE {
327 * encryptionAlgorithm EncryptionAlgorithmIdentifier,
328 * encryptedData EncryptedData
331 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
333 * EncryptedData ::= OCTET STRING
336 * AlgorithmIdentifier ::= SEQUENCE {
337 * algorithm OBJECT IDENTIFIER,
338 * parameters ANY DEFINED BY algorithm OPTIONAL
342 * PBEParameter ::= SEQUENCE {
343 * salt OCTET STRING SIZE(8),
344 * iterationCount INTEGER
347 public class EncryptedPrivateKeyInfo {
349 private string _algorithm;
350 private byte[] _salt;
351 private int _iterations;
352 private byte[] _data;
354 public EncryptedPrivateKeyInfo () {}
356 public EncryptedPrivateKeyInfo (byte[] data) : this ()
363 public string Algorithm {
364 get { return _algorithm; }
365 set { _algorithm = value; }
368 public byte[] EncryptedData {
369 get { return (_data == null) ? null : (byte[]) _data.Clone (); }
370 set { _data = (value == null) ? null : (byte[]) value.Clone (); }
376 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
377 _salt = new byte [8];
378 rng.GetBytes (_salt);
380 return (byte[]) _salt.Clone ();
382 set { _salt = (byte[]) value.Clone (); }
385 public int IterationCount {
386 get { return _iterations; }
389 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
396 private void Decode (byte[] data)
398 ASN1 encryptedPrivateKeyInfo = new ASN1 (data);
399 if (encryptedPrivateKeyInfo.Tag != 0x30)
400 throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
402 ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0];
403 if (encryptionAlgorithm.Tag != 0x30)
404 throw new CryptographicException ("invalid encryptionAlgorithm");
405 ASN1 algorithm = encryptionAlgorithm [0];
406 if (algorithm.Tag != 0x06)
407 throw new CryptographicException ("invalid algorithm");
408 _algorithm = ASN1Convert.ToOid (algorithm);
409 // parameters ANY DEFINED BY algorithm OPTIONAL
410 if (encryptionAlgorithm.Count > 1) {
411 ASN1 parameters = encryptionAlgorithm [1];
412 if (parameters.Tag != 0x30)
413 throw new CryptographicException ("invalid parameters");
415 ASN1 salt = parameters [0];
416 if (salt.Tag != 0x04)
417 throw new CryptographicException ("invalid salt");
420 ASN1 iterationCount = parameters [1];
421 if (iterationCount.Tag != 0x02)
422 throw new CryptographicException ("invalid iterationCount");
423 _iterations = ASN1Convert.ToInt32 (iterationCount);
426 ASN1 encryptedData = encryptedPrivateKeyInfo [1];
427 if (encryptedData.Tag != 0x04)
428 throw new CryptographicException ("invalid EncryptedData");
429 _data = encryptedData.Value;
432 // Note: PKCS#8 doesn't define how to generate the key required for encryption
433 // so you're on your own. Just don't try to copy the big guys too much ;)
434 // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
435 // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
436 public byte[] GetBytes ()
438 if (_algorithm == null)
439 throw new CryptographicException ("No algorithm OID specified");
441 ASN1 encryptionAlgorithm = new ASN1 (0x30);
442 encryptionAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
444 // parameters ANY DEFINED BY algorithm OPTIONAL
445 if ((_iterations > 0) || (_salt != null)) {
446 ASN1 salt = new ASN1 (0x04, _salt);
447 ASN1 iterations = ASN1Convert.FromInt32 (_iterations);
449 ASN1 parameters = new ASN1 (0x30);
450 parameters.Add (salt);
451 parameters.Add (iterations);
452 encryptionAlgorithm.Add (parameters);
455 // encapsulates EncryptedData into an OCTET STRING
456 ASN1 encryptedData = new ASN1 (0x04, _data);
458 ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30);
459 encryptedPrivateKeyInfo.Add (encryptionAlgorithm);
460 encryptedPrivateKeyInfo.Add (encryptedData);
462 return encryptedPrivateKeyInfo.GetBytes ();