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 // Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Security.Cryptography;
36 using Mono.Security.X509;
38 namespace Mono.Security.Cryptography {
57 static public KeyInfo GetType (byte[] data)
60 throw new ArgumentNullException ("data");
62 KeyInfo ki = KeyInfo.Unknown;
64 ASN1 top = new ASN1 (data);
65 if ((top.Tag == 0x30) && (top.Count > 0)) {
66 ASN1 firstLevel = top [0];
67 switch (firstLevel.Tag) {
69 ki = KeyInfo.PrivateKey;
72 ki = KeyInfo.EncryptedPrivateKey;
78 throw new CryptographicException ("invalid ASN.1 data");
84 * PrivateKeyInfo ::= SEQUENCE {
86 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
87 * privateKey PrivateKey,
88 * attributes [0] IMPLICIT Attributes OPTIONAL
93 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
95 * PrivateKey ::= OCTET STRING
97 * Attributes ::= SET OF Attribute
99 public class PrivateKeyInfo {
101 private int _version;
102 private string _algorithm;
104 private ArrayList _list;
106 public PrivateKeyInfo ()
109 _list = new ArrayList ();
112 public PrivateKeyInfo (byte[] data) : this ()
119 public string Algorithm {
120 get { return _algorithm; }
121 set { _algorithm = value; }
124 public ArrayList Attributes {
125 get { return _list; }
128 public byte[] PrivateKey {
132 return (byte[]) _key.Clone ();
136 throw new ArgumentNullException ("PrivateKey");
137 _key = (byte[]) value.Clone ();
142 get { return _version; }
145 throw new ArgumentOutOfRangeException ("negative version");
152 private void Decode (byte[] data)
154 ASN1 privateKeyInfo = new ASN1 (data);
155 if (privateKeyInfo.Tag != 0x30)
156 throw new CryptographicException ("invalid PrivateKeyInfo");
158 ASN1 version = privateKeyInfo [0];
159 if (version.Tag != 0x02)
160 throw new CryptographicException ("invalid version");
161 _version = version.Value [0];
163 ASN1 privateKeyAlgorithm = privateKeyInfo [1];
164 if (privateKeyAlgorithm.Tag != 0x30)
165 throw new CryptographicException ("invalid algorithm");
167 ASN1 algorithm = privateKeyAlgorithm [0];
168 if (algorithm.Tag != 0x06)
169 throw new CryptographicException ("missing algorithm OID");
170 _algorithm = ASN1Convert.ToOid (algorithm);
172 ASN1 privateKey = privateKeyInfo [2];
173 _key = privateKey.Value;
175 // attributes [0] IMPLICIT Attributes OPTIONAL
176 if (privateKeyInfo.Count > 3) {
177 ASN1 attributes = privateKeyInfo [3];
178 for (int i=0; i < attributes.Count; i++) {
179 _list.Add (attributes [i]);
184 public byte[] GetBytes ()
186 ASN1 privateKeyAlgorithm = new ASN1 (0x30);
187 privateKeyAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
188 privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL
190 ASN1 pki = new ASN1 (0x30);
191 pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version }));
192 pki.Add (privateKeyAlgorithm);
193 pki.Add (new ASN1 (0x04, _key));
195 if (_list.Count > 0) {
196 ASN1 attributes = new ASN1 (0xA0);
197 foreach (ASN1 attribute in _list) {
198 attributes.Add (attribute);
200 pki.Add (attributes);
203 return pki.GetBytes ();
208 static private byte[] RemoveLeadingZero (byte[] bigInt)
211 int length = bigInt.Length;
212 if (bigInt [0] == 0x00) {
216 byte[] bi = new byte [length];
217 Buffer.BlockCopy (bigInt, start, bi, 0, length);
221 static private byte[] Normalize (byte[] bigInt, int length)
223 if (bigInt.Length == length)
225 else if (bigInt.Length > length)
226 return RemoveLeadingZero (bigInt);
229 byte[] bi = new byte [length];
230 Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
236 * RSAPrivateKey ::= SEQUENCE {
238 * modulus INTEGER, -- n
239 * publicExponent INTEGER, -- e
240 * privateExponent INTEGER, -- d
241 * prime1 INTEGER, -- p
242 * prime2 INTEGER, -- q
243 * exponent1 INTEGER, -- d mod (p-1)
244 * exponent2 INTEGER, -- d mod (q-1)
245 * coefficient INTEGER, -- (inverse of q) mod p
246 * otherPrimeInfos OtherPrimeInfos OPTIONAL
249 static public RSA DecodeRSA (byte[] keypair)
251 ASN1 privateKey = new ASN1 (keypair);
252 if (privateKey.Tag != 0x30)
253 throw new CryptographicException ("invalid private key format");
255 ASN1 version = privateKey [0];
256 if (version.Tag != 0x02)
257 throw new CryptographicException ("missing version");
259 if (privateKey.Count < 9)
260 throw new CryptographicException ("not enough key parameters");
262 RSAParameters param = new RSAParameters ();
263 // note: MUST remove leading 0 - else MS wont import the key
264 param.Modulus = RemoveLeadingZero (privateKey [1].Value);
265 int keysize = param.Modulus.Length;
266 int keysize2 = (keysize >> 1); // half-size
267 // size must be normalized - else MS wont import the key
268 param.D = Normalize (privateKey [3].Value, keysize);
269 param.DP = Normalize (privateKey [6].Value, keysize2);
270 param.DQ = Normalize (privateKey [7].Value, keysize2);
271 param.Exponent = RemoveLeadingZero (privateKey [2].Value);
272 param.InverseQ = Normalize (privateKey [8].Value, keysize2);
273 param.P = Normalize (privateKey [4].Value, keysize2);
274 param.Q = Normalize (privateKey [5].Value, keysize2);
279 rsa.ImportParameters (param);
281 catch (CryptographicException) {
282 // this may cause problem when this code is run under
283 // the SYSTEM identity on Windows (e.g. ASP.NET). See
284 // http://bugzilla.ximian.com/show_bug.cgi?id=77559
285 CspParameters csp = new CspParameters ();
286 csp.Flags = CspProviderFlags.UseMachineKeyStore;
287 rsa = new RSACryptoServiceProvider (csp);
288 rsa.ImportParameters (param);
294 * RSAPrivateKey ::= SEQUENCE {
296 * modulus INTEGER, -- n
297 * publicExponent INTEGER, -- e
298 * privateExponent INTEGER, -- d
299 * prime1 INTEGER, -- p
300 * prime2 INTEGER, -- q
301 * exponent1 INTEGER, -- d mod (p-1)
302 * exponent2 INTEGER, -- d mod (q-1)
303 * coefficient INTEGER, -- (inverse of q) mod p
304 * otherPrimeInfos OtherPrimeInfos OPTIONAL
307 static public byte[] Encode (RSA rsa)
309 RSAParameters param = rsa.ExportParameters (true);
311 ASN1 rsaPrivateKey = new ASN1 (0x30);
312 rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 }));
313 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus));
314 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent));
315 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D));
316 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P));
317 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q));
318 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP));
319 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ));
320 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ));
322 return rsaPrivateKey.GetBytes ();
325 // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
326 // which isn't enough for rebuilding the keypair. The other parameters
327 // can be found (98% of the time) in the X.509 certificate associated
328 // with the private key or (2% of the time) the parameters are in it's
329 // issuer X.509 certificate (not supported in the .NET framework).
330 static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters)
332 ASN1 pvk = new ASN1 (privateKey);
334 throw new CryptographicException ("invalid private key format");
336 // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
337 dsaParameters.X = Normalize (pvk.Value, 20);
338 DSA dsa = DSA.Create ();
339 dsa.ImportParameters (dsaParameters);
343 static public byte[] Encode (DSA dsa)
345 DSAParameters param = dsa.ExportParameters (true);
346 return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes ();
349 static public byte[] Encode (AsymmetricAlgorithm aa)
352 return Encode ((RSA)aa);
354 return Encode ((DSA)aa);
356 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
361 * EncryptedPrivateKeyInfo ::= SEQUENCE {
362 * encryptionAlgorithm EncryptionAlgorithmIdentifier,
363 * encryptedData EncryptedData
366 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
368 * EncryptedData ::= OCTET STRING
371 * AlgorithmIdentifier ::= SEQUENCE {
372 * algorithm OBJECT IDENTIFIER,
373 * parameters ANY DEFINED BY algorithm OPTIONAL
377 * PBEParameter ::= SEQUENCE {
378 * salt OCTET STRING SIZE(8),
379 * iterationCount INTEGER
382 public class EncryptedPrivateKeyInfo {
384 private string _algorithm;
385 private byte[] _salt;
386 private int _iterations;
387 private byte[] _data;
389 public EncryptedPrivateKeyInfo () {}
391 public EncryptedPrivateKeyInfo (byte[] data) : this ()
398 public string Algorithm {
399 get { return _algorithm; }
400 set { _algorithm = value; }
403 public byte[] EncryptedData {
404 get { return (_data == null) ? null : (byte[]) _data.Clone (); }
405 set { _data = (value == null) ? null : (byte[]) value.Clone (); }
411 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
412 _salt = new byte [8];
413 rng.GetBytes (_salt);
415 return (byte[]) _salt.Clone ();
417 set { _salt = (byte[]) value.Clone (); }
420 public int IterationCount {
421 get { return _iterations; }
424 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
431 private void Decode (byte[] data)
433 ASN1 encryptedPrivateKeyInfo = new ASN1 (data);
434 if (encryptedPrivateKeyInfo.Tag != 0x30)
435 throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
437 ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0];
438 if (encryptionAlgorithm.Tag != 0x30)
439 throw new CryptographicException ("invalid encryptionAlgorithm");
440 ASN1 algorithm = encryptionAlgorithm [0];
441 if (algorithm.Tag != 0x06)
442 throw new CryptographicException ("invalid algorithm");
443 _algorithm = ASN1Convert.ToOid (algorithm);
444 // parameters ANY DEFINED BY algorithm OPTIONAL
445 if (encryptionAlgorithm.Count > 1) {
446 ASN1 parameters = encryptionAlgorithm [1];
447 if (parameters.Tag != 0x30)
448 throw new CryptographicException ("invalid parameters");
450 ASN1 salt = parameters [0];
451 if (salt.Tag != 0x04)
452 throw new CryptographicException ("invalid salt");
455 ASN1 iterationCount = parameters [1];
456 if (iterationCount.Tag != 0x02)
457 throw new CryptographicException ("invalid iterationCount");
458 _iterations = ASN1Convert.ToInt32 (iterationCount);
461 ASN1 encryptedData = encryptedPrivateKeyInfo [1];
462 if (encryptedData.Tag != 0x04)
463 throw new CryptographicException ("invalid EncryptedData");
464 _data = encryptedData.Value;
467 // Note: PKCS#8 doesn't define how to generate the key required for encryption
468 // so you're on your own. Just don't try to copy the big guys too much ;)
469 // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
470 // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
471 public byte[] GetBytes ()
473 if (_algorithm == null)
474 throw new CryptographicException ("No algorithm OID specified");
476 ASN1 encryptionAlgorithm = new ASN1 (0x30);
477 encryptionAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
479 // parameters ANY DEFINED BY algorithm OPTIONAL
480 if ((_iterations > 0) || (_salt != null)) {
481 ASN1 salt = new ASN1 (0x04, _salt);
482 ASN1 iterations = ASN1Convert.FromInt32 (_iterations);
484 ASN1 parameters = new ASN1 (0x30);
485 parameters.Add (salt);
486 parameters.Add (iterations);
487 encryptionAlgorithm.Add (parameters);
490 // encapsulates EncryptedData into an OCTET STRING
491 ASN1 encryptedData = new ASN1 (0x04, _data);
493 ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30);
494 encryptedPrivateKeyInfo.Add (encryptionAlgorithm);
495 encryptedPrivateKeyInfo.Add (encryptedData);
497 return encryptedPrivateKeyInfo.GetBytes ();