2 // PKCS1.cs - Implements PKCS#1 primitives.
5 // Sebastien Pouliot <sebastien@xamarin.com>
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 // Copyright 2013 Xamarin Inc. (http://www.xamarin.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.Security.Cryptography;
34 namespace Mono.Security.Cryptography {
37 // a. PKCS#1: RSA Cryptography Standard
38 // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
51 private static bool Compare (byte[] array1, byte[] array2)
53 bool result = (array1.Length == array2.Length);
55 for (int i=0; i < array1.Length; i++)
56 if (array1[i] != array2[i])
62 private static byte[] xor (byte[] array1, byte[] array2)
64 byte[] result = new byte [array1.Length];
65 for (int i=0; i < result.Length; i++)
66 result[i] = (byte) (array1[i] ^ array2[i]);
70 private static byte[] emptySHA1 = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 };
71 private static byte[] emptySHA256 = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 };
72 private static byte[] emptySHA384 = { 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b };
73 private static byte[] emptySHA512 = { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e };
75 private static byte[] GetEmptyHash (HashAlgorithm hash)
79 else if (hash is SHA256)
81 else if (hash is SHA384)
83 else if (hash is SHA512)
86 return hash.ComputeHash ((byte[])null);
89 // PKCS #1 v.2.1, Section 4.1
90 // I2OSP converts a non-negative integer to an octet string of a specified length.
91 public static byte[] I2OSP (int x, int size)
93 byte[] array = BitConverterLE.GetBytes (x);
94 Array.Reverse (array, 0, array.Length);
95 return I2OSP (array, size);
98 public static byte[] I2OSP (byte[] x, int size)
100 byte[] result = new byte [size];
101 Buffer.BlockCopy (x, 0, result, (result.Length - x.Length), x.Length);
105 // PKCS #1 v.2.1, Section 4.2
106 // OS2IP converts an octet string to a nonnegative integer.
107 public static byte[] OS2IP (byte[] x)
110 while ((x [i++] == 0x00) && (i < x.Length)) {
111 // confuse compiler into reporting a warning with {}
115 byte[] result = new byte [x.Length - i];
116 Buffer.BlockCopy (x, i, result, 0, result.Length);
123 // PKCS #1 v.2.1, Section 5.1.1
124 public static byte[] RSAEP (RSA rsa, byte[] m)
127 return rsa.EncryptValue (m);
130 // PKCS #1 v.2.1, Section 5.1.2
131 public static byte[] RSADP (RSA rsa, byte[] c)
134 // Decrypt value may apply CRT optimizations
135 return rsa.DecryptValue (c);
138 // PKCS #1 v.2.1, Section 5.2.1
139 public static byte[] RSASP1 (RSA rsa, byte[] m)
141 // first form: s = m^d mod n
142 // Decrypt value may apply CRT optimizations
143 return rsa.DecryptValue (m);
146 // PKCS #1 v.2.1, Section 5.2.2
147 public static byte[] RSAVP1 (RSA rsa, byte[] s)
150 return rsa.EncryptValue (s);
153 // PKCS #1 v.2.1, Section 7.1.1
154 // RSAES-OAEP-ENCRYPT ((n, e), M, L)
155 public static byte[] Encrypt_OAEP (RSA rsa, HashAlgorithm hash, RandomNumberGenerator rng, byte[] M)
157 int size = rsa.KeySize / 8;
158 int hLen = hash.HashSize / 8;
159 if (M.Length > size - 2 * hLen - 2)
160 throw new CryptographicException ("message too long");
161 // empty label L SHA1 hash
162 byte[] lHash = GetEmptyHash (hash);
163 int PSLength = (size - M.Length - 2 * hLen - 2);
164 // DB = lHash || PS || 0x01 || M
165 byte[] DB = new byte [lHash.Length + PSLength + 1 + M.Length];
166 Buffer.BlockCopy (lHash, 0, DB, 0, lHash.Length);
167 DB [(lHash.Length + PSLength)] = 0x01;
168 Buffer.BlockCopy (M, 0, DB, (DB.Length - M.Length), M.Length);
170 byte[] seed = new byte [hLen];
173 byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
174 byte[] maskedDB = xor (DB, dbMask);
175 byte[] seedMask = MGF1 (hash, maskedDB, hLen);
176 byte[] maskedSeed = xor (seed, seedMask);
177 // EM = 0x00 || maskedSeed || maskedDB
178 byte[] EM = new byte [maskedSeed.Length + maskedDB.Length + 1];
179 Buffer.BlockCopy (maskedSeed, 0, EM, 1, maskedSeed.Length);
180 Buffer.BlockCopy (maskedDB, 0, EM, maskedSeed.Length + 1, maskedDB.Length);
182 byte[] m = OS2IP (EM);
183 byte[] c = RSAEP (rsa, m);
184 return I2OSP (c, size);
187 // PKCS #1 v.2.1, Section 7.1.2
188 // RSAES-OAEP-DECRYPT (K, C, L)
189 public static byte[] Decrypt_OAEP (RSA rsa, HashAlgorithm hash, byte[] C)
191 int size = rsa.KeySize / 8;
192 int hLen = hash.HashSize / 8;
193 if ((size < (2 * hLen + 2)) || (C.Length != size))
194 throw new CryptographicException ("decryption error");
196 byte[] c = OS2IP (C);
197 byte[] m = RSADP (rsa, c);
198 byte[] EM = I2OSP (m, size);
200 // split EM = Y || maskedSeed || maskedDB
201 byte[] maskedSeed = new byte [hLen];
202 Buffer.BlockCopy (EM, 1, maskedSeed, 0, maskedSeed.Length);
203 byte[] maskedDB = new byte [size - hLen - 1];
204 Buffer.BlockCopy (EM, (EM.Length - maskedDB.Length), maskedDB, 0, maskedDB.Length);
206 byte[] seedMask = MGF1 (hash, maskedDB, hLen);
207 byte[] seed = xor (maskedSeed, seedMask);
208 byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
209 byte[] DB = xor (maskedDB, dbMask);
211 byte[] lHash = GetEmptyHash (hash);
212 // split DB = lHash' || PS || 0x01 || M
213 byte[] dbHash = new byte [lHash.Length];
214 Buffer.BlockCopy (DB, 0, dbHash, 0, dbHash.Length);
215 bool h = Compare (lHash, dbHash);
217 // find separator 0x01
218 int nPos = lHash.Length;
219 while (DB[nPos] == 0)
222 int Msize = DB.Length - nPos - 1;
223 byte[] M = new byte [Msize];
224 Buffer.BlockCopy (DB, (nPos + 1), M, 0, Msize);
226 // we could have returned EM[0] sooner but would be helping a timing attack
227 if ((EM[0] != 0) || (!h) || (DB[nPos] != 0x01))
232 // PKCS #1 v.2.1, Section 7.2.1
233 // RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
234 public static byte[] Encrypt_v15 (RSA rsa, RandomNumberGenerator rng, byte[] M)
236 int size = rsa.KeySize / 8;
237 if (M.Length > size - 11)
238 throw new CryptographicException ("message too long");
239 int PSLength = System.Math.Max (8, (size - M.Length - 3));
240 byte[] PS = new byte [PSLength];
241 rng.GetNonZeroBytes (PS);
242 byte[] EM = new byte [size];
244 Buffer.BlockCopy (PS, 0, EM, 2, PSLength);
245 Buffer.BlockCopy (M, 0, EM, (size - M.Length), M.Length);
247 byte[] m = OS2IP (EM);
248 byte[] c = RSAEP (rsa, m);
249 byte[] C = I2OSP (c, size);
253 // PKCS #1 v.2.1, Section 7.2.2
254 // RSAES-PKCS1-V1_5-DECRYPT (K, C)
255 public static byte[] Decrypt_v15 (RSA rsa, byte[] C)
257 int size = rsa.KeySize >> 3; // div by 8
258 if ((size < 11) || (C.Length > size))
259 throw new CryptographicException ("decryption error");
260 byte[] c = OS2IP (C);
261 byte[] m = RSADP (rsa, c);
262 byte[] EM = I2OSP (m, size);
264 if ((EM [0] != 0x00) || (EM [1] != 0x02))
268 // PS is a minimum of 8 bytes + 2 bytes for header
269 while ((EM [mPos] != 0x00) && (mPos < EM.Length))
271 if (EM [mPos] != 0x00)
274 byte[] M = new byte [EM.Length - mPos];
275 Buffer.BlockCopy (EM, mPos, M, 0, M.Length);
279 // PKCS #1 v.2.1, Section 8.2.1
280 // RSASSA-PKCS1-V1_5-SIGN (K, M)
281 public static byte[] Sign_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue)
283 int size = (rsa.KeySize >> 3); // div 8
284 byte[] EM = Encode_v15 (hash, hashValue, size);
285 byte[] m = OS2IP (EM);
286 byte[] s = RSASP1 (rsa, m);
287 byte[] S = I2OSP (s, size);
291 internal static byte[] Sign_v15 (RSA rsa, string hashName, byte[] hashValue)
293 using (var hash = CreateFromName (hashName))
294 return Sign_v15 (rsa, hash, hashValue);
297 // PKCS #1 v.2.1, Section 8.2.2
298 // RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)
299 public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue, byte[] signature)
301 return Verify_v15 (rsa, hash, hashValue, signature, false);
304 internal static bool Verify_v15 (RSA rsa, string hashName, byte[] hashValue, byte[] signature)
306 using (var hash = CreateFromName (hashName))
307 return Verify_v15 (rsa, hash, hashValue, signature, false);
310 // DO NOT USE WITHOUT A VERY GOOD REASON
311 public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte [] hashValue, byte [] signature, bool tryNonStandardEncoding)
313 int size = (rsa.KeySize >> 3); // div 8
314 byte[] s = OS2IP (signature);
315 byte[] m = RSAVP1 (rsa, s);
316 byte[] EM2 = I2OSP (m, size);
317 byte[] EM = Encode_v15 (hash, hashValue, size);
318 bool result = Compare (EM, EM2);
319 if (result || !tryNonStandardEncoding)
322 // NOTE: some signatures don't include the hash OID (pretty lame but real)
323 // and compatible with MS implementation. E.g. Verisign Authenticode Timestamps
325 // we're making this "as safe as possible"
326 if ((EM2 [0] != 0x00) || (EM2 [1] != 0x01))
329 for (i = 2; i < EM2.Length - hashValue.Length - 1; i++) {
333 if (EM2 [i++] != 0x00)
336 byte [] decryptedHash = new byte [hashValue.Length];
337 Buffer.BlockCopy (EM2, i, decryptedHash, 0, decryptedHash.Length);
338 return Compare (decryptedHash, hashValue);
341 // PKCS #1 v.2.1, Section 9.2
342 // EMSA-PKCS1-v1_5-Encode
343 public static byte[] Encode_v15 (HashAlgorithm hash, byte[] hashValue, int emLength)
345 if (hashValue.Length != (hash.HashSize >> 3))
346 throw new CryptographicException ("bad hash length for " + hash.ToString ());
348 // DigestInfo ::= SEQUENCE {
349 // digestAlgorithm AlgorithmIdentifier,
350 // digest OCTET STRING
355 string oid = CryptoConfig.MapNameToOID (hash.ToString ());
358 ASN1 digestAlgorithm = new ASN1 (0x30);
359 digestAlgorithm.Add (new ASN1 (CryptoConfig.EncodeOID (oid)));
360 digestAlgorithm.Add (new ASN1 (0x05)); // NULL
361 ASN1 digest = new ASN1 (0x04, hashValue);
362 ASN1 digestInfo = new ASN1 (0x30);
363 digestInfo.Add (digestAlgorithm);
364 digestInfo.Add (digest);
366 t = digestInfo.GetBytes ();
370 // There are no valid OID, in this case t = hashValue
371 // This is the case of the MD5SHA hash algorithm
375 Buffer.BlockCopy (hashValue, 0, t, t.Length - hashValue.Length, hashValue.Length);
377 int PSLength = System.Math.Max (8, emLength - t.Length - 3);
378 // PS = PSLength of 0xff
380 // EM = 0x00 | 0x01 | PS | 0x00 | T
381 byte[] EM = new byte [PSLength + t.Length + 3];
383 for (int i=2; i < PSLength + 2; i++)
385 Buffer.BlockCopy (t, 0, EM, PSLength + 3, t.Length);
390 // PKCS #1 v.2.1, Section B.2.1
391 public static byte[] MGF1 (HashAlgorithm hash, byte[] mgfSeed, int maskLen)
393 // 1. If maskLen > 2^32 hLen, output "mask too long" and stop.
394 // easy - this is impossible by using a int (31bits) as parameter ;-)
395 // BUT with a signed int we do have to check for negative values!
397 throw new OverflowException();
399 int mgfSeedLength = mgfSeed.Length;
400 int hLen = (hash.HashSize >> 3); // from bits to bytes
401 int iterations = (maskLen / hLen);
402 if (maskLen % hLen != 0)
404 // 2. Let T be the empty octet string.
405 byte[] T = new byte [iterations * hLen];
407 byte[] toBeHashed = new byte [mgfSeedLength + 4];
409 // 3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following:
410 for (int counter = 0; counter < iterations; counter++) {
411 // a. Convert counter to an octet string C of length 4 octets
412 byte[] C = I2OSP (counter, 4);
414 // b. Concatenate the hash of the seed mgfSeed and C to the octet string T:
415 // T = T || Hash (mgfSeed || C)
416 Buffer.BlockCopy (mgfSeed, 0, toBeHashed, 0, mgfSeedLength);
417 Buffer.BlockCopy (C, 0, toBeHashed, mgfSeedLength, 4);
418 byte[] output = hash.ComputeHash (toBeHashed);
419 Buffer.BlockCopy (output, 0, T, pos, hLen);
423 // 4. Output the leading maskLen octets of T as the octet string mask.
424 byte[] mask = new byte [maskLen];
425 Buffer.BlockCopy (T, 0, mask, 0, maskLen);
429 static internal string HashNameFromOid (string oid, bool throwOnError = true)
432 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
434 case "1.2.840.113549.1.1.3": // MD4 with RSA encryption
436 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
438 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
439 case "1.3.14.3.2.29": // SHA1 with RSA signature
440 case "1.2.840.10040.4.3": // SHA1-1 with DSA
442 case "1.2.840.113549.1.1.11": // SHA-256 with RSA Encryption
444 case "1.2.840.113549.1.1.12": // SHA-384 with RSA Encryption
446 case "1.2.840.113549.1.1.13": // SHA-512 with RSA Encryption
448 case "1.3.36.3.3.1.2":
452 throw new CryptographicException ("Unsupported hash algorithm: " + oid);
457 static internal HashAlgorithm CreateFromOid (string oid)
459 return CreateFromName (HashNameFromOid (oid));
462 static internal HashAlgorithm CreateFromName (string name)
467 return MD2.Create ();
469 return MD4.Create ();
471 return MD5.Create ();
473 return SHA1.Create ();
475 return SHA256.Create ();
477 return SHA384.Create ();
479 return SHA512.Create ();
481 return RIPEMD160.Create ();
484 return (HashAlgorithm) Activator.CreateInstance (Type.GetType (name));
487 throw new CryptographicException ("Unsupported hash algorithm: " + name);
491 return HashAlgorithm.Create (name);