| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- //
- // PKCS1.cs - Implements PKCS#1 primitives.
- //
- // Author:
- // Sebastien Pouliot ([email protected])
- //
- // (C) 2002 Motus Technologies Inc. (http://www.motus.com)
- //
- using System;
- namespace System.Security.Cryptography {
- // References:
- // a. PKCS#1: RSA Cryptography Standard
- // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
- internal class PKCS1 {
- private static bool Compare (byte[] array1, byte[] array2)
- {
- bool result = (array1.Length == array2.Length);
- if (result) {
- for (int i=0; i < array1.Length; i++)
- if (array1[i] != array2[i])
- return false;
- }
- return result;
- }
- private static byte[] xor (byte[] array1, byte[] array2)
- {
- byte[] result = new byte [array1.Length];
- for (int i=0; i < result.Length; i++)
- result[i] = (byte) (array1[i] ^ array2[i]);
- return result;
- }
- private static byte[] emptySHA1 = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 };
- 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 };
- 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 };
- 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 };
- private static byte[] GetEmptyHash (HashAlgorithm hash)
- {
- if (hash is SHA1)
- return emptySHA1;
- else if (hash is SHA256)
- return emptySHA256;
- else if (hash is SHA384)
- return emptySHA384;
- else if (hash is SHA512)
- return emptySHA512;
- else
- return null;
- }
- // PKCS #1 v.2.1, Section 4.1
- // I2OSP converts a nonnegative integer to an octet string of a specified length.
- public static byte[] I2OSP (int x, int size)
- {
- byte[] array = BitConverter.GetBytes (x);
- return I2OSP (array, size);
- }
- public static byte[] I2OSP (byte[] x, int size)
- {
- byte[] result = new byte [size];
- Array.Copy (x, 0, result, (result.Length - x.Length), x.Length);
- return result;
- }
- // PKCS #1 v.2.1, Section 4.2
- // OS2IP converts an octet string to a nonnegative integer.
- public static byte[] OS2IP (byte[] x)
- {
- int i = 0;
- while ((x[i++] == 0x00) && (i < x.Length));
- i--;
- if (i > 0) {
- byte[] result = new byte [x.Length - i];
- Array.Copy (x, i, result, 0, result.Length);
- return result;
- }
- else
- return x;
- }
- // PKCS #1 v.2.1, Section 5.1.1
- public static byte[] RSAEP (RSA rsa, byte[] m)
- {
- // c = m^e mod n
- return rsa.EncryptValue (m);
- }
- // PKCS #1 v.2.1, Section 5.1.2
- public static byte[] RSADP (RSA rsa, byte[] c)
- {
- // m = c^d mod n
- // Decrypt value may apply CRT optimizations
- return rsa.DecryptValue (c);
- }
- // PKCS #1 v.2.1, Section 5.2.1
- public static byte[] RSASP1 (RSA rsa, byte[] m)
- {
- // first form: s = m^d mod n
- // Decrypt value may apply CRT optimizations
- return rsa.DecryptValue (m);
- }
- // PKCS #1 v.2.1, Section 5.2.2
- public static byte[] RSAVP1 (RSA rsa, byte[] s)
- {
- // m = s^e mod n
- return rsa.EncryptValue (s);
- }
- // PKCS #1 v.2.1, Section 7.1.1
- // RSAES-OAEP-ENCRYPT ((n, e), M, L)
- public static byte[] Encrypt_OAEP (RSA rsa, HashAlgorithm hash, RandomNumberGenerator rng, byte[] M)
- {
- int size = rsa.KeySize / 8;
- int hLen = hash.HashSize / 8;
- if (M.Length > size - 2 * hLen - 2)
- throw new CryptographicException ("message too long");
- // empty label L SHA1 hash
- byte[] lHash = GetEmptyHash (hash);
- int PSLength = (size - M.Length - 2 * hLen - 2);
- // DB = lHash || PS || 0x01 || M
- byte[] DB = new byte [lHash.Length + PSLength + 1 + M.Length];
- Array.Copy (lHash, 0, DB, 0, lHash.Length);
- DB [(lHash.Length + PSLength)] = 0x01;
- Array.Copy (M, 0, DB, (DB.Length - M.Length), M.Length);
- byte[] seed = new byte [hLen];
- rng.GetBytes (seed);
- byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
- byte[] maskedDB = xor (DB, dbMask);
- byte[] seedMask = MGF1 (hash, maskedDB, hLen);
- byte[] maskedSeed = xor (seed, seedMask);
- // EM = 0x00 || maskedSeed || maskedDB
- byte[] EM = new byte [maskedSeed.Length + maskedDB.Length + 1];
- Array.Copy (maskedSeed, 0, EM, 1, maskedSeed.Length);
- Array.Copy (maskedDB, 0, EM, maskedSeed.Length + 1, maskedDB.Length);
- byte[] m = OS2IP (EM);
- byte[] c = RSAEP (rsa, m);
- return I2OSP (c, size);
- }
- // PKCS #1 v.2.1, Section 7.1.2
- // RSAES-OAEP-DECRYPT (K, C, L)
- public static byte[] Decrypt_OAEP (RSA rsa, HashAlgorithm hash, byte[] C)
- {
- int size = rsa.KeySize / 8;
- int hLen = hash.HashSize / 8;
- if ((size < (2 * hLen + 2)) || (C.Length != size))
- throw new CryptographicException ("decryption error");
- byte[] c = OS2IP (C);
- byte[] m = RSADP (rsa, c);
- byte[] EM = I2OSP (m, size);
- // split EM = Y || maskedSeed || maskedDB
- byte[] maskedSeed = new byte [hLen];
- Array.Copy (EM, 1, maskedSeed, 0, maskedSeed.Length);
- byte[] maskedDB = new byte [size - hLen - 1];
- Array.Copy (EM, (EM.Length - maskedDB.Length), maskedDB, 0, maskedDB.Length);
- byte[] seedMask = MGF1 (hash, maskedDB, hLen);
- byte[] seed = xor (maskedSeed, seedMask);
- byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
- byte[] DB = xor (maskedDB, dbMask);
- byte[] lHash = GetEmptyHash (hash);
- // split DB = lHash’ || PS || 0x01 || M
- byte[] dbHash = new byte [lHash.Length];
- Array.Copy (DB, 0, dbHash, 0, dbHash.Length);
- bool h = Compare (lHash, dbHash);
- // find separator 0x01
- int nPos = lHash.Length;
- while (DB[nPos] == 0)
- nPos++;
- int Msize = DB.Length - nPos - 1;
- byte[] M = new byte [Msize];
- Array.Copy (DB, (nPos + 1), M, 0, Msize);
- // we could have returned EM[0] sooner but would be helping a timing attack
- if ((EM[0] != 0) || (!h) || (DB[nPos] != 0x01))
- return null;
- return M;
- }
- // PKCS #1 v.2.1, Section 7.2.1
- // RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
- public static byte[] Encrypt_v15 (RSA rsa, RandomNumberGenerator rng, byte[] M)
- {
- int size = rsa.KeySize / 8;
- if (M.Length > size - 11)
- throw new CryptographicException ("message too long");
- int PSLength = Math.Max (8, (size - M.Length - 3));
- byte[] PS = new byte [PSLength];
- rng.GetNonZeroBytes (PS);
- byte[] EM = new byte [size];
- EM [1] = 0x02;
- Array.Copy (PS, 0, EM, 2, PSLength);
- Array.Copy (M, 0, EM, (size - M.Length), M.Length);
- byte[] m = OS2IP (EM);
- byte[] c = RSAEP (rsa, m);
- byte[] C = I2OSP (c, size);
- return C;
- }
- // PKCS #1 v.2.1, Section 7.2.2
- // RSAES-PKCS1-V1_5-DECRYPT (K, C)
- public static byte[] Decrypt_v15 (RSA rsa, byte[] C)
- {
- int size = rsa.KeySize / 8;
- if ((size < 11) || (C.Length != size))
- throw new CryptographicException ("decryption error");
- byte[] c = OS2IP (C);
- byte[] m = RSADP (rsa, c);
- byte[] EM = I2OSP (m, size);
- if ((EM [0] != 0x00) || (EM [1] != 0x02))
- return null;
- int mPos = 10;
- // PS is a minimum of 8 bytes + 2 bytes for header
- while ((EM [mPos] != 0x00) && (mPos < EM.Length))
- mPos++;
- if (EM [mPos] != 0x00)
- return null;
- mPos++;
- byte[] M = new byte [EM.Length - mPos];
- Array.Copy (EM, mPos, M, 0, M.Length);
- return M;
- }
- // PKCS #1 v.2.1, Section 8.2.1
- // RSASSA-PKCS1-V1_5-SIGN (K, M)
- public static byte[] Sign_v15 (RSA rsa, string oid, byte[] hash)
- {
- int size = rsa.KeySize / 8;
- byte[] EM = Encode_v15 (oid, hash, size);
- byte[] m = OS2IP (EM);
- byte[] s = RSASP1 (rsa, m);
- byte[] S = I2OSP (s, size);
- return S;
- }
- // PKCS #1 v.2.1, Section 8.2.2
- // RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)
- public static bool Verify_v15 (RSA rsa, string oid, byte[] hash, byte[] signature)
- {
- int size = rsa.KeySize / 8;
- byte[] s = OS2IP (signature);
- byte[] m = RSAVP1 (rsa, signature);
- byte[] EM2 = I2OSP (m, size);
- byte[] EM = Encode_v15 (oid, hash, size);
- return Compare(EM, EM2);
- }
- // Note: MD2 isn't supported in .NET framework
- private static byte[] md2const = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10 };
- private static byte[] md5const = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 };
- private static byte[] sha1const = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
- private static byte[] sha256const = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
- private static byte[] sha384const = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
- private static byte[] sha512const = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
- // PKCS #1 v.2.1, Section 9.2
- // EMSA-PKCS1-v1_5-Encode
- public static byte[] Encode_v15 (string oid, byte[] hash, int emLength)
- {
- string e = "bad hash length for ";
- byte[] t = null;
- switch (oid) {
- case "1.3.14.3.2.26":
- // SHA1
- if (hash.Length != 20)
- throw new CryptographicException (e + oid);
- t = new byte [35];
- Array.Copy (sha1const, 0, t, 0, sha1const.Length);
- break;
- case "1.2.840.113549.2.2":
- // MD2
- if (hash.Length != 16)
- throw new CryptographicException (e + oid);
- t = new byte [34];
- Array.Copy (md2const, 0, t, 0, md2const.Length);
- break;
- case "1.2.840.113549.2.5":
- // MD5
- if (hash.Length != 16)
- throw new CryptographicException (e + oid);
- t = new byte [34];
- Array.Copy (md5const, 0, t, 0, md5const.Length);
- break;
- case "2.16.840.1.101.3.4.1":
- // SHA256
- if (hash.Length != 32)
- throw new CryptographicException (e + oid);
- t = new byte [51];
- Array.Copy (sha256const, 0, t, 0, sha256const.Length);
- break;
- case "2.16.840.1.101.3.4.2":
- // SHA384
- if (hash.Length != 48)
- throw new CryptographicException (e + oid);
- t = new byte [67];
- Array.Copy (sha384const, 0, t, 0, sha384const.Length);
- break;
- case "2.16.840.1.101.3.4.3":
- // SHA512
- if (hash.Length != 64)
- throw new CryptographicException (e + oid);
- t = new byte [83];
- Array.Copy (sha512const, 0, t, 0, sha512const.Length);
- break;
- default:
- return null;
- }
- Array.Copy (hash, 0, t, t.Length - hash.Length, hash.Length);
- int PSLength = Math.Max (8, emLength - t.Length - 3);
- // PS = PSLength of 0xff
- // EM = 0x00 | 0x01 | PS | 0x00 | T
- byte[] EM = new byte [PSLength + t.Length + 3];
- EM [1] = 0x01;
- for (int i=2; i < PSLength + 2; i++)
- EM[i] = 0xff;
- Array.Copy (t, 0, EM, PSLength + 3, t.Length);
- return EM;
- }
- // PKCS #1 v.2.1, Section B.2.1
- public static byte[] MGF1 (HashAlgorithm hash, byte[] mgfSeed, int maskLen)
- {
- // 1. If maskLen > 2^32 hLen, output “mask too long” and stop.
- // easy - this is impossible by using a int (31bits) as parameter ;-)
- // BUT with a signed int we do have to check for negative values!
- if (maskLen < 0)
- throw new OverflowException();
- int mgfSeedLength = mgfSeed.Length;
- int hLen = (hash.HashSize >> 3); // from bits to bytes
- int iterations = (maskLen / hLen);
- if (maskLen % hLen != 0)
- iterations++;
- // 2. Let T be the empty octet string.
- byte[] T = new byte [iterations * hLen];
- byte[] toBeHashed = new byte [mgfSeedLength + 4];
- int pos = 0;
- // 3. For counter from 0 to (maskLen / hLen) – 1, do the following:
- for (int counter = 0; counter < iterations; counter++) {
- // a. Convert counter to an octet string C of length 4 octets
- byte[] C = I2OSP (counter, 4);
- // b. Concatenate the hash of the seed mgfSeed and C to the octet string T:
- // T = T || Hash (mgfSeed || C)
- Array.Copy (mgfSeed, 0, toBeHashed, 0, mgfSeedLength);
- Array.Copy (C, 0, toBeHashed, mgfSeedLength, 4);
- byte[] output = hash.ComputeHash (toBeHashed);
- Array.Copy (output, 0, T, pos, hLen);
- pos += mgfSeedLength;
- }
-
- // 4. Output the leading maskLen octets of T as the octet string mask.
- byte[] mask = new byte [maskLen];
- Array.Copy (T, 0, mask, 0, maskLen);
- return mask;
- }
- }
- }
|