2 // PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard
3 // ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc
6 // Sebastien Pouliot (spouliot@motus.com)
8 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
12 using System.Collections;
13 using System.Security.Cryptography;
16 using Mono.Security.Cryptography;
17 using Mono.Security.X509;
19 namespace Mono.Security.Cryptography {
29 static public KeyInfo GetType (byte[] data)
32 throw new ArgumentNullException ("data");
34 KeyInfo ki = KeyInfo.Unknown;
36 ASN1 top = new ASN1 (data);
37 if ((top.Tag == 0x30) && (top.Count > 0)) {
38 ASN1 firstLevel = top [0];
39 switch (firstLevel.Tag) {
41 ki = KeyInfo.PrivateKey;
44 ki = KeyInfo.EncryptedPrivateKey;
50 throw new CryptographicException ("invalid ASN.1 data");
56 * PrivateKeyInfo ::= SEQUENCE {
58 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
59 * privateKey PrivateKey,
60 * attributes [0] IMPLICIT Attributes OPTIONAL
65 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
67 * PrivateKey ::= OCTET STRING
69 * Attributes ::= SET OF Attribute
71 public class PrivateKeyInfo {
74 private string _algorithm;
76 private ArrayList _list;
78 public PrivateKeyInfo ()
81 _list = new ArrayList ();
84 public PrivateKeyInfo (byte[] data) : this ()
91 public string Algorithm {
92 get { return _algorithm; }
93 set { _algorithm = value; }
96 public ArrayList Attributes {
100 public byte[] PrivateKey {
102 set { _key = value; }
106 get { return _version; }
109 throw new ArgumentOutOfRangeException ("negative version");
116 private void Decode (byte[] data)
118 ASN1 privateKeyInfo = new ASN1 (data);
119 if (privateKeyInfo.Tag != 0x30)
120 throw new CryptographicException ("invalid PrivateKeyInfo");
122 ASN1 version = privateKeyInfo [0];
123 if (version.Tag != 0x02)
124 throw new CryptographicException ("invalid version");
125 _version = version.Value [0];
127 ASN1 privateKeyAlgorithm = privateKeyInfo [1];
128 if (privateKeyAlgorithm.Tag != 0x30)
129 throw new CryptographicException ("invalid algorithm");
131 ASN1 algorithm = privateKeyAlgorithm [0];
132 if (algorithm.Tag != 0x06)
133 throw new CryptographicException ("missing algorithm OID");
134 _algorithm = ASN1Convert.ToOID (algorithm);
136 ASN1 privateKey = privateKeyInfo [2];
137 _key = privateKey.Value;
139 // attributes [0] IMPLICIT Attributes OPTIONAL
140 if (privateKeyInfo.Count > 3) {
141 ASN1 attributes = privateKeyInfo [3];
142 for (int i=0; i < attributes.Count; i++) {
143 _list.Add (attributes [i]);
148 public byte[] GetBytes ()
150 ASN1 privateKeyAlgorithm = new ASN1 (0x30);
151 privateKeyAlgorithm.Add (ASN1Convert.FromOID (_algorithm));
152 privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL
154 ASN1 pki = new ASN1 (0x30);
155 pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version }));
156 pki.Add (privateKeyAlgorithm);
157 pki.Add (new ASN1 (0x04, _key));
159 if (_list.Count > 0) {
160 ASN1 attributes = new ASN1 (0xA0);
161 foreach (ASN1 attribute in _list) {
162 attributes.Add (attribute);
164 pki.Add (attributes);
167 return pki.GetBytes ();
172 static private byte[] RemoveLeadingZero (byte[] bigInt)
175 int length = bigInt.Length;
176 if (bigInt [0] == 0x00) {
180 byte[] bi = new byte [length];
181 Buffer.BlockCopy (bigInt, start, bi, 0, length);
185 static private byte[] Normalize (byte[] bigInt, int length)
187 if (bigInt.Length == length)
189 else if (bigInt.Length > length)
190 return RemoveLeadingZero (bigInt);
193 byte[] bi = new byte [length];
194 Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
200 * RSAPrivateKey ::= SEQUENCE {
202 * modulus INTEGER, -- n
203 * publicExponent INTEGER, -- e
204 * privateExponent INTEGER, -- d
205 * prime1 INTEGER, -- p
206 * prime2 INTEGER, -- q
207 * exponent1 INTEGER, -- d mod (p-1)
208 * exponent2 INTEGER, -- d mod (q-1)
209 * coefficient INTEGER, -- (inverse of q) mod p
210 * otherPrimeInfos OtherPrimeInfos OPTIONAL
213 static public RSA DecodeRSA (byte[] keypair)
215 ASN1 privateKey = new ASN1 (keypair);
216 if (privateKey.Tag != 0x30)
217 throw new CryptographicException ("invalid private key format");
219 ASN1 version = privateKey [0];
220 if (version.Tag != 0x02)
221 throw new CryptographicException ("missing version");
223 if (privateKey.Count < 9)
224 throw new CryptographicException ("not enough key parameters");
226 RSAParameters param = new RSAParameters ();
227 // note: MUST remove leading 0 - else MS wont import the key
228 param.Modulus = RemoveLeadingZero (privateKey [1].Value);
229 int keysize = param.Modulus.Length;
230 int keysize2 = (keysize >> 1); // half-size
231 // size must be normalized - else MS wont import the key
232 param.D = Normalize (privateKey [3].Value, keysize);
233 param.DP = Normalize (privateKey [6].Value, keysize2);
234 param.DQ = Normalize (privateKey [7].Value, keysize2);
235 param.Exponent = RemoveLeadingZero (privateKey [2].Value);
236 param.InverseQ = Normalize (privateKey [8].Value, keysize2);
237 param.P = Normalize (privateKey [4].Value, keysize2);
238 param.Q = Normalize (privateKey [5].Value, keysize2);
240 RSA rsa = RSA.Create ();
241 rsa.ImportParameters (param);
246 * RSAPrivateKey ::= SEQUENCE {
248 * modulus INTEGER, -- n
249 * publicExponent INTEGER, -- e
250 * privateExponent INTEGER, -- d
251 * prime1 INTEGER, -- p
252 * prime2 INTEGER, -- q
253 * exponent1 INTEGER, -- d mod (p-1)
254 * exponent2 INTEGER, -- d mod (q-1)
255 * coefficient INTEGER, -- (inverse of q) mod p
256 * otherPrimeInfos OtherPrimeInfos OPTIONAL
259 static public byte[] Encode (RSA rsa)
261 RSAParameters param = rsa.ExportParameters (true);
263 ASN1 rsaPrivateKey = new ASN1 (0x30);
264 rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 }));
265 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus));
266 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent));
267 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D));
268 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P));
269 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q));
270 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP));
271 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ));
272 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ));
274 return rsaPrivateKey.GetBytes ();
277 // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
278 // which isn't enough for rebuilding the keypair. The other parameters
279 // can be found (98% of the time) in the X.509 certificate associated
280 // with the private key or (2% of the time) the parameters are in it's
281 // issuer X.509 certificate (not supported in the .NET framework).
282 static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters)
284 ASN1 pvk = new ASN1 (privateKey);
286 throw new CryptographicException ("invalid private key format");
288 // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
289 dsaParameters.X = Normalize (privateKey, 20);
290 DSA dsa = DSA.Create ();
291 dsa.ImportParameters (dsaParameters);
295 static public byte[] Encode (DSA dsa)
297 DSAParameters param = dsa.ExportParameters (true);
298 return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes ();
301 static public byte[] Encode (AsymmetricAlgorithm aa)
304 return Encode ((RSA)aa);
306 return Encode ((DSA)aa);
308 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
313 * EncryptedPrivateKeyInfo ::= SEQUENCE {
314 * encryptionAlgorithm EncryptionAlgorithmIdentifier,
315 * encryptedData EncryptedData
318 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
320 * EncryptedData ::= OCTET STRING
323 * AlgorithmIdentifier ::= SEQUENCE {
324 * algorithm OBJECT IDENTIFIER,
325 * parameters ANY DEFINED BY algorithm OPTIONAL
329 * PBEParameter ::= SEQUENCE {
330 * salt OCTET STRING SIZE(8),
331 * iterationCount INTEGER
334 public class EncryptedPrivateKeyInfo {
336 private string _algorithm;
337 private byte[] _salt;
338 private int _iterations;
339 private byte[] _data;
341 public EncryptedPrivateKeyInfo () {}
343 public EncryptedPrivateKeyInfo (byte[] data) : this ()
350 public string Algorithm {
351 get { return _algorithm; }
352 set { _algorithm = value; }
355 public byte[] EncryptedData {
356 get { return (_data == null) ? null : (byte[]) _data.Clone (); }
357 set { _data = (value == null) ? null : (byte[]) value.Clone (); }
363 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
364 _salt = new byte [8];
365 rng.GetBytes (_salt);
367 return (byte[]) _salt.Clone ();
369 set { _salt = (byte[]) value.Clone (); }
372 public int IterationCount {
373 get { return _iterations; }
376 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
383 private void Decode (byte[] data)
385 ASN1 encryptedPrivateKeyInfo = new ASN1 (data);
386 if (encryptedPrivateKeyInfo.Tag != 0x30)
387 throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
389 ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0];
390 if (encryptionAlgorithm.Tag != 0x30)
391 throw new CryptographicException ("invalid encryptionAlgorithm");
392 ASN1 algorithm = encryptionAlgorithm [0];
393 if (algorithm.Tag != 0x06)
394 throw new CryptographicException ("invalid algorithm");
395 _algorithm = ASN1Convert.ToOID (algorithm);
396 // parameters ANY DEFINED BY algorithm OPTIONAL
397 if (encryptionAlgorithm.Count > 1) {
398 ASN1 parameters = encryptionAlgorithm [1];
399 if (parameters.Tag != 0x30)
400 throw new CryptographicException ("invalid parameters");
402 ASN1 salt = parameters [0];
403 if (salt.Tag != 0x04)
404 throw new CryptographicException ("invalid salt");
407 ASN1 iterationCount = parameters [1];
408 if (iterationCount.Tag != 0x02)
409 throw new CryptographicException ("invalid iterationCount");
410 _iterations = ASN1Convert.ToInt32 (iterationCount);
413 ASN1 encryptedData = encryptedPrivateKeyInfo [1];
414 if (encryptedData.Tag != 0x04)
415 throw new CryptographicException ("invalid EncryptedData");
416 _data = encryptedData.Value;
419 // Note: PKCS#8 doesn't define how to generate the key required for encryption
420 // so you're on your own. Just don't try to copy the big guys too much ;)
421 // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
422 // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
423 public byte[] GetBytes ()
425 if (_algorithm == null)
426 throw new CryptographicException ("No algorithm OID specified");
428 ASN1 encryptionAlgorithm = new ASN1 (0x30);
429 encryptionAlgorithm.Add (ASN1Convert.FromOID (_algorithm));
431 // parameters ANY DEFINED BY algorithm OPTIONAL
432 if ((_iterations > 0) || (_salt != null)) {
433 ASN1 salt = new ASN1 (0x04, _salt);
434 ASN1 iterations = ASN1Convert.FromInt32 (_iterations);
436 ASN1 parameters = new ASN1 (0x30);
437 parameters.Add (salt);
438 parameters.Add (iterations);
439 encryptionAlgorithm.Add (parameters);
442 // encapsulates EncryptedData into an OCTET STRING
443 ASN1 encryptedData = new ASN1 (0x04, _data);
445 ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30);
446 encryptedPrivateKeyInfo.Add (encryptionAlgorithm);
447 encryptedPrivateKeyInfo.Add (encryptedData);
449 return encryptedPrivateKeyInfo.GetBytes ();