📄 rsacryptoserviceprovider.cs
字号:
/* * RSACryptoServiceProvider.cs - Implementation of the * "System.Security.Cryptography.RSACryptoServiceProvider" class. * * Copyright (C) 2002 Southern Storm Software, Pty Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */namespace System.Security.Cryptography{#if CONFIG_CRYPTOusing System;using System.IO;using Platform;// Note: this class assumes that the RSA parameters can be accessed// in main memory. This may not necessarily be the case if the// system is using smart cards or some other kind of crypto dongle.// We will modify this class when and if that becomes an issue.//// This implementation is based on the RSA description in PKCS #1 v2.1,// available from "http://www.rsa.com/".public sealed class RSACryptoServiceProvider : RSA{ // Internal state. private bool persistKey; private static bool useMachineKeyStore; private RSAParameters rsaParams; // Constructors. public RSACryptoServiceProvider() : this(0, null) {} public RSACryptoServiceProvider(CspParameters parameters) : this(0, parameters) {} public RSACryptoServiceProvider(int dwKeySize) : this(dwKeySize, null) {} public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters) { byte[] key; int result; // See "DSACryptoServiceProvider" for explainatory comments. if(parameters != null && parameters.KeyContainerName != null) { // Attempt to get an RSA key from the user's keychain. key = CryptoMethods.GetKey(CryptoMethods.RSAEncrypt, parameters.KeyContainerName, parameters.Flags, out result); if(key != null) { // The "ASN1ToPublic" method will determine if // the key is X.509, bare public, or private. rsaParams.ASN1ToPublic(key, 0, key.Length); Array.Clear(key, 0, key.Length); persistKey = true; } else if(result == CryptoMethods.UnknownKey) { throw new CryptographicException (_("Crypto_UnknownKey"), parameters.KeyContainerName); } else if(result == CryptoMethods.NotPermitted) { throw new CryptographicException (_("Crypto_NoKeyAccess"), parameters.KeyContainerName); } else if(result == CryptoMethods.GenerateKey) { // Generate a new key for the user. // TODO } } } // Destructor. ~RSACryptoServiceProvider() { Dispose(false); } // Dispose this algorithm instance. protected override void Dispose(bool disposing) { rsaParams.Clear(); } // Get the name of the key exchange algorithm. public override String KeyExchangeAlgorithm { get { return "RSA-PKCS1-KeyEx"; } } // Get or set the size of the key modulus, in bits. public override int KeySize { get { return KeySizeValue; } set { base.KeySize = value; } } // Determine if the key should be persisted in the CSP. public bool PersistKeyInCsp { get { return persistKey; } set { persistKey = value; } } // Get the name of the signature algorithm. public override String SignatureAlgorithm { get { // W3C identifier for the RSA-SHA1 algorithm. return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; } } // Determine if we should use the machine key store. public static bool UseMachineKeyStore { get { return useMachineKeyStore; } set { useMachineKeyStore = value; } } // Apply the public key to a value. private byte[] ApplyPublic(byte[] value) { if(rsaParams.Modulus == null) { throw new CryptographicException (_("Crypto_RSAParamsNotSet")); } return CryptoMethods.NumPow(value, rsaParams.Exponent, rsaParams.Modulus); } // Apply the private key to a value. private byte[] ApplyPrivate(byte[] value) { if(rsaParams.P != null) { // Use the Chinese Remainder Theorem exponents. // Based on the description in PKCS #1. byte[] m1 = CryptoMethods.NumPow(value, rsaParams.DP, rsaParams.P); byte[] m2 = CryptoMethods.NumPow(value, rsaParams.DQ, rsaParams.Q); byte[] diff = CryptoMethods.NumSub(m1, m2, null); byte[] h = CryptoMethods.NumMul(diff, rsaParams.InverseQ, rsaParams.P); byte[] prod = CryptoMethods.NumMul(rsaParams.Q, h, null); byte[] m = CryptoMethods.NumAdd(m2, prod, null); // Clear all temporary values. Array.Clear(m1, 0, m1.Length); Array.Clear(m2, 0, m2.Length); Array.Clear(diff, 0, diff.Length); Array.Clear(h, 0, h.Length); Array.Clear(prod, 0, prod.Length); // Return the decrypted message. return m; } else if(rsaParams.D != null) { // Use the private exponent directly. return CryptoMethods.NumPow(value, rsaParams.D, rsaParams.Modulus); } else { // Insufficient parameters for private key operations. throw new CryptographicException (_("Crypto_RSAParamsNotSet")); } } // Decrypt a value using the RSA private key and OAEP padding. internal byte[] DecryptOAEP(byte[] rgb) { // Check the data against null. if(rgb == null) { throw new ArgumentNullException("rgb"); } // Make sure that we have sufficient RSA parameters. if(rsaParams.Modulus == null) { throw new CryptographicException (_("Crypto_RSAParamsNotSet")); } // Check the length of the incoming value. int k = rsaParams.Modulus.Length; if(k < (2 * 20 + 2)) { throw new CryptographicException (_("Crypto_RSAKeyTooShort")); } // Decrypt the incoming value, and expand to k octets, // as the big number routines in the engine may have // stripped leading zeroes from the value. byte[] decrypted = ApplyPrivate(rgb); if(decrypted.Length > k) { Array.Clear(decrypted, 0, decrypted.Length); throw new CryptographicException (_("Crypto_RSAInvalidCiphertext")); } byte[] msg = new byte [k]; Array.Copy(decrypted, 0, msg, k - decrypted.Length, decrypted.Length); // Acquire a mask generation method, based on SHA-1. MaskGenerationMethod maskGen = new PKCS1MaskGenerationMethod(); // Extract the non-seed part of the decrypted message. byte[] maskedMsg = new byte [k - 20 - 1]; Array.Copy(msg, 0, maskedMsg, 0, k - 20 - 1); // Decrypt the seed value. byte[] seedMask = maskGen.GenerateMask(maskedMsg, 20); byte[] seed = new byte [20]; int index; for(index = 0; index < 20; ++index) { seed[index] = (byte)(msg[index + 1] ^ seedMask[index]); } // Decrypt the non-seed part of the decrypted message. byte[] msgMask = maskGen.GenerateMask(seed, maskedMsg.Length); for(index = 0; index < maskedMsg.Length; ++index) { maskedMsg[index] ^= msgMask[index]; } // Validate the format of the unmasked message. We do this // carefully, to prevent attackers from performing timing // attacks on the algorithm. See PKCS #1 for details. int error = msg[0]; for(index = 0; index < 20; ++index) { error |= (maskedMsg[index] ^ label[index]); } for(index = 20; index < (k - 20 - 2); ++index) { // Question: is this careful enough to prevent // timing attacks? May need revisiting later. if(maskedMsg[index] != 0) { break; } } error |= (maskedMsg[index] ^ 0x01); if(error != 0) { // Something is wrong with the decrypted padding data. Array.Clear(decrypted, 0, decrypted.Length); Array.Clear(msg, 0, msg.Length); Array.Clear(maskedMsg, 0, maskedMsg.Length); Array.Clear(seedMask, 0, seedMask.Length); Array.Clear(seed, 0, seed.Length); Array.Clear(msgMask, 0, msgMask.Length); throw new CryptographicException (_("Crypto_RSAInvalidCiphertext")); } ++index; // Extract the final decrypted message. byte[] finalMsg = new byte [maskedMsg.Length - index]; Array.Copy(maskedMsg, index, finalMsg, 0, maskedMsg.Length - index); // Destroy sensitive values. Array.Clear(decrypted, 0, decrypted.Length); Array.Clear(msg, 0, msg.Length); Array.Clear(maskedMsg, 0, maskedMsg.Length); Array.Clear(seedMask, 0, seedMask.Length); Array.Clear(seed, 0, seed.Length); Array.Clear(msgMask, 0, msgMask.Length); // Done. return finalMsg; } // Decrypt a value using the RSA private key and PKCS1 decoding. internal byte[] DecryptPKCS1(byte[] rgb) { // Check the data against null. if(rgb == null) { throw new ArgumentNullException("rgb"); } // Make sure that we have sufficient RSA parameters. if(rsaParams.Modulus == null) { throw new CryptographicException (_("Crypto_RSAParamsNotSet")); } // Check the length of the incoming value. int k = rsaParams.Modulus.Length; if(k < 11) { throw new CryptographicException (_("Crypto_RSAKeyTooShort")); } // Decrypt the incoming value, and expand to k octets, // as the big number routines in the engine may have // stripped leading zeroes from the value. byte[] decrypted = ApplyPrivate(rgb); if(decrypted.Length > k) { Array.Clear(decrypted, 0, decrypted.Length); throw new CryptographicException (_("Crypto_RSAInvalidCiphertext")); } byte[] msg = new byte [k]; Array.Copy(decrypted, 0, msg, k - decrypted.Length, decrypted.Length); // Check the format of the padding data. We need to // harden this against timing attacks. It is hard to // do so since we don't know the expected length of // the final message. int error = msg[0] | (msg[1] ^ 0x02); int index = 2; while(index < (msg.Length - 1) && msg[index] != 0x00) { ++index; } error |= msg[index]; if(error != 0) { Array.Clear(decrypted, 0, decrypted.Length); Array.Clear(msg, 0, msg.Length); throw new CryptographicException (_("Crypto_RSAInvalidCiphertext")); } ++index; // Extract the final message. byte[] finalMsg = new byte [msg.Length - index]; Array.Copy(msg, index, finalMsg, 0, finalMsg.Length); // Destroy sensitive values. Array.Clear(decrypted, 0, decrypted.Length); Array.Clear(msg, 0, msg.Length); // Done. return finalMsg; } // Decrypt a value using the RSA private key and optional OAEP padding. public byte[] Decrypt(byte[] rgb, bool fOAEP) { if(fOAEP) { return DecryptOAEP(rgb); } else { return DecryptPKCS1(rgb); } } // Decrypt a value using the RSA private key. public override byte[] DecryptValue(byte[] rgb) { return DecryptPKCS1(rgb); } // Label value for OAEP encryption. SHA-1 hash of the empty string. private static readonly byte[] label = {(byte)0xDA, (byte)0x39, (byte)0xA3, (byte)0xEE, (byte)0x5E, (byte)0x6B, (byte)0x4B, (byte)0x0D, (byte)0x32, (byte)0x55, (byte)0xBF, (byte)0xEF, (byte)0x95, (byte)0x60, (byte)0x18, (byte)0x90, (byte)0xAF, (byte)0xD8, (byte)0x07, (byte)0x09}; // Encrypt a value using a specified OAEP padding array. // The array may be null to pad with zeroes. internal byte[] EncryptOAEP(byte[] rgb, byte[] padding, RandomNumberGenerator rng) { // Check the data against null. if(rgb == null) { throw new ArgumentNullException("rgb"); } // Make sure that we have sufficient RSA parameters. if(rsaParams.Modulus == null) { throw new CryptographicException (_("Crypto_RSAParamsNotSet")); } // Format the label, padding string, and rgb into a message. int k = rsaParams.Modulus.Length; if(rgb.Length > (k - 2 * 20 - 2)) { throw new CryptographicException (_("Crypto_RSAMessageTooLong")); } byte[] msg = new byte [k - 20 - 1]; int posn = 0; Array.Copy(label, 0, msg, posn, 20); posn += 20; int padlen = k - rgb.Length - 2 * 20 - 2; if(padlen > 0 && padding != null) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -