Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
CryptoUtils.cs1167 linesDownload Raw Back to utils
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Diagnostics.CodeAnalysis;5using System.IO;6using System.Linq;7using System.Management.Automation.Remoting;8using System.Runtime.Serialization;9using System.Security;10using System.Security.Cryptography;11using System.Text;12using System.Threading;13 14using Dbg = System.Management.Automation.Diagnostics;15 16namespace System.Management.Automation.Internal17{18    /// <summary>19    /// This class provides the converters for all Native CAPI key blob formats.20    /// </summary>21    internal static class PSCryptoNativeConverter22    {23        #region Constants24 25        /// <summary>26        /// The blob version is fixed.27        /// </summary>28        public const uint CUR_BLOB_VERSION = 0x00000002;29 30        /// <summary>31        /// RSA Key.32        /// </summary>33        public const uint CALG_RSA_KEYX = 0x000000a4;34 35        /// <summary>36        /// AES 256 symmetric key.37        /// </summary>38        public const uint CALG_AES_256 = 0x00000010;39 40        /// <summary>41        /// Option for exporting public key blob.42        /// </summary>43        public const uint PUBLICKEYBLOB = 0x00000006;44 45        /// <summary>46        /// PUBLICKEYBLOB header length.47        /// </summary>48        public const int PUBLICKEYBLOB_HEADER_LEN = 20;49 50        /// <summary>51        /// Option for exporting a session key.52        /// </summary>53        public const uint SIMPLEBLOB = 0x00000001;54 55        /// <summary>56        /// SIMPLEBLOB header length.57        /// </summary>58        public const int SIMPLEBLOB_HEADER_LEN = 12;59 60        #endregion Constants61 62        #region Functions63 64        private static int ToInt32LE(byte[] bytes, int offset)65        {66            return (bytes[offset + 3] << 24) | (bytes[offset + 2] << 16) | (bytes[offset + 1] << 8) | bytes[offset];67        }68 69        private static uint ToUInt32LE(byte[] bytes, int offset)70        {71            return (uint)((bytes[offset + 3] << 24) | (bytes[offset + 2] << 16) | (bytes[offset + 1] << 8) | bytes[offset]);72        }73 74        private static byte[] GetBytesLE(int val)75        {76            return new[] {77                (byte)(val & 0xff),78                (byte)((val >> 8) & 0xff),79                (byte)((val >> 16) & 0xff),80                (byte)((val >> 24) & 0xff)81            };82        }83 84        private static byte[] CreateReverseByteArray(byte[] data)85        {86            byte[] reverseData = new byte[data.Length];87            Array.Copy(data, reverseData, data.Length);88            Array.Reverse(reverseData);89            return reverseData;90        }91 92        internal static RSA FromCapiPublicKeyBlob(byte[] blob)93        {94            return FromCapiPublicKeyBlob(blob, 0);95        }96 97        private static RSA FromCapiPublicKeyBlob(byte[] blob, int offset)98        {99            ArgumentNullException.ThrowIfNull(blob);100 101            if (offset > blob.Length)102            {103                throw new ArgumentException(SecuritySupportStrings.InvalidOffset);104            }105 106            var rsap = GetParametersFromCapiPublicKeyBlob(blob, offset);107 108            try109            {110                RSA rsa = RSA.Create();111                rsa.ImportParameters(rsap);112                return rsa;113            }114            catch (Exception ex)115            {116                throw new CryptographicException(SecuritySupportStrings.CannotImportPublicKey, ex);117            }118        }119 120        private static RSAParameters GetParametersFromCapiPublicKeyBlob(byte[] blob, int offset)121        {122            ArgumentNullException.ThrowIfNull(blob);123 124            if (offset > blob.Length)125            {126                throw new ArgumentException(SecuritySupportStrings.InvalidOffset);127            }128 129            if (blob.Length < PUBLICKEYBLOB_HEADER_LEN)130            {131                throw new ArgumentException(SecuritySupportStrings.InvalidPublicKey);132            }133 134            try135            {136                if ((blob[offset] != PUBLICKEYBLOB) ||            // PUBLICKEYBLOB (0x06)137                    (blob[offset + 1] != CUR_BLOB_VERSION) ||       // Version (0x02)138                    (blob[offset + 2] != 0x00) ||                   // Reserved (word)139                    (blob[offset + 3] != 0x00) ||140                    (ToUInt32LE(blob, offset + 8) != 0x31415352))   // DWORD magic = RSA1141                {142                    throw new CryptographicException(SecuritySupportStrings.InvalidPublicKey);143                }144 145                // DWORD bitlen146                int bitLen = ToInt32LE(blob, offset + 12);147 148                // DWORD public exponent149                RSAParameters rsap = new RSAParameters();150                rsap.Exponent = new byte[3];151                rsap.Exponent[0] = blob[offset + 18];152                rsap.Exponent[1] = blob[offset + 17];153                rsap.Exponent[2] = blob[offset + 16];154 155                int pos = offset + 20;156                int byteLen = (bitLen >> 3);157                rsap.Modulus = new byte[byteLen];158                Buffer.BlockCopy(blob, pos, rsap.Modulus, 0, byteLen);159                Array.Reverse(rsap.Modulus);160 161                return rsap;162            }163            catch (Exception ex)164            {165                throw new CryptographicException(SecuritySupportStrings.InvalidPublicKey, ex);166            }167        }168 169        internal static byte[] ToCapiPublicKeyBlob(RSA rsa)170        {171            ArgumentNullException.ThrowIfNull(rsa);172 173            RSAParameters p = rsa.ExportParameters(false);174            int keyLength = p.Modulus.Length;   // in bytes175            byte[] blob = new byte[PUBLICKEYBLOB_HEADER_LEN + keyLength];176 177            blob[0] = (byte)PUBLICKEYBLOB;      // Type - PUBLICKEYBLOB (0x06)178            blob[1] = (byte)CUR_BLOB_VERSION;   // Version - Always CUR_BLOB_VERSION (0x02)179            // [2], [3]                         // RESERVED - Always 0180            blob[5] = (byte)CALG_RSA_KEYX;      // ALGID - Always 00 a4 00 00 (for CALG_RSA_KEYX)181            blob[8] = 0x52;                     // Magic - RSA1 (ASCII in hex)182            blob[9] = 0x53;183            blob[10] = 0x41;184            blob[11] = 0x31;185 186            byte[] bitlen = GetBytesLE(keyLength << 3);187            blob[12] = bitlen[0];               // bitlen188            blob[13] = bitlen[1];189            blob[14] = bitlen[2];190            blob[15] = bitlen[3];191 192            // public exponent (DWORD)193            int pos = 16;194            int n = p.Exponent.Length;195 196            Dbg.Assert(n <= 4, "RSA exponent byte length cannot exceed allocated segment");197 198            while (n > 0)199            {200                blob[pos++] = p.Exponent[--n];201            }202 203            // modulus204            pos = 20;205            byte[] key = p.Modulus;206            Array.Reverse(key);207            Buffer.BlockCopy(key, 0, blob, pos, keyLength);208 209            return blob;210        }211 212        internal static byte[] FromCapiSimpleKeyBlob(byte[] blob)213        {214            ArgumentNullException.ThrowIfNull(blob);215 216            if (blob.Length < SIMPLEBLOB_HEADER_LEN)217            {218                throw new ArgumentException(SecuritySupportStrings.InvalidSessionKey);219            }220 221            // just ignore the header of the capi blob and go straight for the key222            return CreateReverseByteArray(blob.Skip(SIMPLEBLOB_HEADER_LEN).ToArray());223        }224 225        internal static byte[] ToCapiSimpleKeyBlob(byte[] encryptedKey)226        {227            ArgumentNullException.ThrowIfNull(encryptedKey);228 229            // formulate the PUBLICKEYSTRUCT230            byte[] blob = new byte[SIMPLEBLOB_HEADER_LEN + encryptedKey.Length];231 232            blob[0] = (byte)SIMPLEBLOB;         // Type - SIMPLEBLOB (0x01)233            blob[1] = (byte)CUR_BLOB_VERSION;   // Version - Always CUR_BLOB_VERSION (0x02)234            // [2], [3]                         // RESERVED - Always 0235            blob[4] = (byte)CALG_AES_256;       // AES-256 algo id (0x10)236            blob[5] = 0x66;                     // ??237            // [6], [7], [8]                    // 0x00238            blob[9] = (byte)CALG_RSA_KEYX;      // 0xa4239            // [10], [11]                       // 0x00240 241            // create a reversed copy and add the encrypted key242            byte[] reversedKey = CreateReverseByteArray(encryptedKey);243            Buffer.BlockCopy(reversedKey, 0, blob, SIMPLEBLOB_HEADER_LEN, reversedKey.Length);244 245            return blob;246        }247 248        #endregion Functions249    }250 251    /// <summary>252    /// Defines a custom exception which is thrown when253    /// a native CAPI call results in an error.254    /// </summary>255    /// <remarks>This exception is currently internal as it's not256    /// surfaced to the user. However, if we decide to surface errors257    /// to the user when something fails on the remote end, then this258    /// can be turned public</remarks>259    [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]260    internal class PSCryptoException : Exception261    {262        #region Private Members263 264        private readonly uint _errorCode;265 266        #endregion Private Members267 268        #region Internal Properties269 270        /// <summary>271        /// Error code returned by the native CAPI call.272        /// </summary>273        internal uint ErrorCode274        {275            get276            {277                return _errorCode;278            }279        }280 281        #endregion Internal Properties282 283        #region Constructors284 285        /// <summary>286        /// Default constructor.287        /// </summary>288        public PSCryptoException()289            : this(0, new StringBuilder(string.Empty)) { }290 291        /// <summary>292        /// Constructor that will be used from within CryptoUtils.293        /// </summary>294        /// <param name="errorCode">error code returned by native295        /// crypto application</param>296        /// <param name="message">Error message associated with this failure.</param>297        public PSCryptoException(uint errorCode, StringBuilder message)298            : base(message.ToString())299        {300            _errorCode = errorCode;301        }302 303        /// <summary>304        /// Constructor with just message but no inner exception.305        /// </summary>306        /// <param name="message">Error message associated with this failure.</param>307        public PSCryptoException(string message)308            : this(message, null) { }309 310        /// <summary>311        /// Constructor with inner exception.312        /// </summary>313        /// <param name="message">Error message.</param>314        /// <param name="innerException">Inner exception.</param>315        /// <remarks>This constructor is currently not called316        /// explicitly from crypto utils</remarks>317        public PSCryptoException(string message, Exception innerException)318            : base(message, innerException)319        {320            _errorCode = unchecked((uint)-1);321        }322 323        /// <summary>324        /// Constructor which has type specific serialization logic.325        /// </summary>326        /// <param name="info">Serialization info.</param>327        /// <param name="context">Context in which this constructor is called.</param>328        /// <remarks>Currently no custom type-specific serialization logic is329        /// implemented</remarks>330        [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")]331        protected PSCryptoException(SerializationInfo info, StreamingContext context)332        {333            throw new NotSupportedException();334        }335 336        #endregion Constructors337    }338 339    /// <summary>340    /// A reverse compatible implementation of session key exchange. This supports the CAPI341    /// keyblob formats but uses dotnet std abstract AES and RSA classes for all crypto operations.342    /// </summary>343    internal sealed class PSRSACryptoServiceProvider : IDisposable344    {345        #region Private Members346 347        // handle session key encryption/decryption348        private RSA _rsa;349 350        // handle to the AES provider object (houses session key and iv)351        private readonly Aes _aes;352 353        // this flag indicates that this class has a key imported from the354        // remote end and so can be used for encryption355        private bool _canEncrypt;356 357        // bool indicating if session key was generated before358        private bool _sessionKeyGenerated = false;359 360        private static readonly object s_syncObject = new object();361 362        #endregion Private Members363 364        #region Constructors365 366        /// <summary>367        /// Private constructor.368        /// </summary>369        /// <param name="serverMode">indicates if this service370        /// provider is operating in server mode</param>371        private PSRSACryptoServiceProvider(bool serverMode)372        {373            if (serverMode)374            {375                GenerateKeyPair();376            }377 378            _aes = Aes.Create();379            _aes.IV = new byte[16];  // iv should be 0380        }381 382        #endregion Constructors383 384        #region Internal Methods385 386        /// <summary>387        /// Get the public key, in CAPI-compatible form, as a base64 encoded string.388        /// </summary>389        /// <returns>Public key as base64 encoded string.</returns>390        internal string GetPublicKeyAsBase64EncodedString()391        {392            Dbg.Assert(_rsa != null, "No public key available.");393 394            byte[] capiPublicKeyBlob = PSCryptoNativeConverter.ToCapiPublicKeyBlob(_rsa);395 396            return Convert.ToBase64String(capiPublicKeyBlob);397        }398 399        /// <summary>400        /// Generates an AEX-256 session key if one is not already generated.401        /// </summary>402        internal void GenerateSessionKey()403        {404            if (_sessionKeyGenerated)405                return;406 407            lock (s_syncObject)408            {409                if (!_sessionKeyGenerated)410                {411                    // Aes object gens key automatically on construction, so this is somewhat redundant,412                    // but at least the actionable key will not be in-memory until it's requested fwiw.413                    _aes.GenerateKey();414                    _sessionKeyGenerated = true;415                    _canEncrypt = true;  // we can encrypt and decrypt once session key is available416                }417            }418        }419 420        /// <summary>421        /// 1. Generate a AES-256 session key422        /// 2. Encrypt the session key with the Imported423        ///    RSA public key424        /// 3. Encode result above as base 64 string and export.425        /// </summary>426        /// <returns>Session key encrypted with receivers public key427        /// and encoded as a base 64 string.</returns>428        internal string SafeExportSessionKey()429        {430            Dbg.Assert(_rsa != null, "No public key available.");431 432            // generate one if not already done.433            GenerateSessionKey();434 435            // encrypt it436            // codeql[cs/cryptography/rsa-unapproved-encryption-padding-scheme] - PowerShell v7.4 and later versions have deprecated the key exchange in the remoting protocol. This code is kept only for backward compatibility reason.437            byte[] encryptedKey = _rsa.Encrypt(_aes.Key, RSAEncryptionPadding.Pkcs1);438 439            // convert the key to capi simpleblob format before exporting440            byte[] simpleKeyBlob = PSCryptoNativeConverter.ToCapiSimpleKeyBlob(encryptedKey);441            return Convert.ToBase64String(simpleKeyBlob);442        }443 444        /// <summary>445        /// Import a public key into the provider whose context446        /// has been obtained.447        /// </summary>448        /// <param name="publicKey">Base64 encoded public key to import.</param>449        internal void ImportPublicKeyFromBase64EncodedString(string publicKey)450        {451            Dbg.Assert(!string.IsNullOrEmpty(publicKey), "key cannot be null or empty");452 453            byte[] publicKeyBlob = Convert.FromBase64String(publicKey);454            _rsa = PSCryptoNativeConverter.FromCapiPublicKeyBlob(publicKeyBlob);455        }456 457        /// <summary>458        /// Import a session key from the remote side into459        /// the current CSP.460        /// </summary>461        /// <param name="sessionKey">encrypted session key as a462        /// base64 encoded string</param>463        internal void ImportSessionKeyFromBase64EncodedString(string sessionKey)464        {465            Dbg.Assert(!string.IsNullOrEmpty(sessionKey), "key cannot be null or empty");466 467            byte[] sessionKeyBlob = Convert.FromBase64String(sessionKey);468            byte[] rsaEncryptedKey = PSCryptoNativeConverter.FromCapiSimpleKeyBlob(sessionKeyBlob);469 470            // codeql[cs/cryptography/rsa-unapproved-encryption-padding-scheme] - PowerShell v7.4 and later versions have deprecated the key exchange in the remoting protocol. This code is kept only for backward compatibility reason.471            _aes.Key = _rsa.Decrypt(rsaEncryptedKey, RSAEncryptionPadding.Pkcs1);472 473            // now we have imported the key and will be able to474            // encrypt using the session key475            _canEncrypt = true;476        }477 478        /// <summary>479        /// Encrypt the specified byte array.480        /// </summary>481        /// <param name="data">Data to encrypt.</param>482        /// <returns>Encrypted byte array.</returns>483        internal byte[] EncryptWithSessionKey(byte[] data)484        {485            Dbg.Assert(_canEncrypt, "Remote key has not been imported to encrypt");486 487            using (ICryptoTransform encryptor = _aes.CreateEncryptor())488            using (MemoryStream targetStream = new MemoryStream())489            using (MemoryStream sourceStream = new MemoryStream(data))490            {491                using (CryptoStream cryptoStream = new CryptoStream(targetStream, encryptor, CryptoStreamMode.Write))492                {493                    sourceStream.CopyTo(cryptoStream);494                }495 496                return targetStream.ToArray();497            }498        }499 500        /// <summary>501        /// Decrypt the specified buffer.502        /// </summary>503        /// <param name="data">Data to decrypt.</param>504        /// <returns>Decrypted buffer.</returns>505        internal byte[] DecryptWithSessionKey(byte[] data)506        {507            using (ICryptoTransform decryptor = _aes.CreateDecryptor())508            using (MemoryStream sourceStream = new MemoryStream(data))509            using (MemoryStream targetStream = new MemoryStream())510            {511                using (CryptoStream csDecrypt = new CryptoStream(sourceStream, decryptor, CryptoStreamMode.Read))512                {513                    csDecrypt.CopyTo(targetStream);514                }515 516                return targetStream.ToArray();517            }518        }519 520        /// <summary>521        /// Generates key pair in a thread safe manner522        /// the first time when required.523        /// </summary>524        internal void GenerateKeyPair()525        {526            _rsa = RSA.Create();527            _rsa.KeySize = 2048;528        }529 530        /// <summary>531        /// Indicates if a key exchange is complete532        /// and this provider can encrypt.533        /// </summary>534        internal bool CanEncrypt535        {536            get537            {538                return _canEncrypt;539            }540 541            set542            {543                _canEncrypt = value;544            }545        }546 547        #endregion Internal Methods548 549        #region Internal Static Methods550 551        /// <summary>552        /// Returns a crypto service provider for use in the553        /// client. This will reuse the key that has been554        /// generated.555        /// </summary>556        /// <returns>Crypto service provider for557        /// the client side.</returns>558        internal static PSRSACryptoServiceProvider GetRSACryptoServiceProviderForClient()559        {560            return new PSRSACryptoServiceProvider(false);561        }562 563        /// <summary>564        /// Returns a crypto service provider for use in the565        /// server. This will not generate a key pair.566        /// </summary>567        /// <returns>Crypto service provider for568        /// the server side.</returns>569        internal static PSRSACryptoServiceProvider GetRSACryptoServiceProviderForServer()570        {571            return new PSRSACryptoServiceProvider(true);572        }573 574        #endregion Internal Static Methods575 576        #region IDisposable577 578        /// <summary>579        /// Release all resources.580        /// </summary>581        public void Dispose()582        {583            _rsa?.Dispose();584            _aes?.Dispose();585        }586 587        #endregion IDisposable588    }589 590    /// <summary>591    /// Helper for exchanging keys and encrypting/decrypting592    /// secure strings for serialization in remoting.593    /// </summary>594    public abstract class PSRemotingCryptoHelper : IDisposable595    {596        #region Protected Members597 598        /// <summary>599        /// Crypto provider which will be used for importing remote600        /// public key as well as generating a session key, exporting601        /// it and performing symmetric key operations using the602        /// session key.603        /// </summary>604        internal PSRSACryptoServiceProvider _rsaCryptoProvider;605 606        /// <summary>607        /// Key exchange has been completed and both keys608        /// available.609        /// </summary>610        protected ManualResetEvent _keyExchangeCompleted = new ManualResetEvent(false);611 612        /// <summary>613        /// Object for synchronizing key exchange.614        /// </summary>615        protected object syncObject = new object();616 617        private bool _keyExchangeStarted = false;618 619        /// <summary>620        /// </summary>621        protected void RunKeyExchangeIfRequired()622        {623            Dbg.Assert(Session != null, "data structure handler not set");624 625            if (!_rsaCryptoProvider.CanEncrypt)626            {627                try628                {629                    lock (syncObject)630                    {631                        if (!_rsaCryptoProvider.CanEncrypt)632                        {633                            if (!_keyExchangeStarted)634                            {635                                _keyExchangeStarted = true;636                                _keyExchangeCompleted.Reset();637                                Session.StartKeyExchange();638                            }639                        }640                    }641                }642                finally643                {644                    // for whatever reason if StartKeyExchange()645                    // throws an exception it should reset the646                    // wait handle, so it should pass this wait647                    // if it doesn't do so, its a bug648                    _keyExchangeCompleted.WaitOne();649                }650            }651        }652 653        /// <summary>654        /// Gets the bytes of a secure string.655        /// </summary>656        private static byte[] GetBytesFromSecureString(SecureString secureString)657        {658            return secureString is null659                ? null660                : Microsoft.PowerShell.SecureStringHelper.GetData(secureString);661        }662 663        /// <summary>664        /// Gets a secure string from the specified byte array.665        /// </summary>666        private static SecureString GetSecureStringFromBytes(byte[] data)667        {668            Dbg.Assert(data is not null, "The passed-in data cannot be null.");669 670            try671            {672                return Microsoft.PowerShell.SecureStringHelper.New(data);673            }674            finally675            {676                // zero out the contents677                Array.Clear(data);678            }679        }680 681        /// <summary>682        /// Convert a secure string to a base64 encoded string.683        /// </summary>684        protected string ConvertSecureStringToBase64String(SecureString secureString)685        {686            string dataAsString = null;687            byte[] data = GetBytesFromSecureString(secureString);688 689            if (data is not null)690            {691                try692                {693                    dataAsString = Convert.ToBase64String(data);694                }695                finally696                {697                    Array.Clear(data);698                }699            }700 701            return dataAsString;702        }703 704        /// <summary>705        /// Convert a base64 encoded string to a secure string.706        /// </summary>707        /// <param name="base64String"></param>708        /// <returns></returns>709        protected SecureString ConvertBase64StringToSecureString(string base64String)710        {711            try712            {713                byte[] data = Convert.FromBase64String(base64String);714                return GetSecureStringFromBytes(data);715            }716            catch (FormatException)717            {718                // do nothing719                // this catch is to ensure that the exception doesn't720                // go unhandled leading to a crash721                throw new PSCryptoException();722            }723        }724 725        /// <summary>726        /// Core logic to encrypt a string. Assumes session key is already generated.727        /// </summary>728        /// <param name="secureString">729        /// secure string to be encrypted730        /// </param>731        /// <returns></returns>732        protected string EncryptSecureStringCore(SecureString secureString)733        {734            string encryptedDataAsString = null;735 736            if (_rsaCryptoProvider.CanEncrypt)737            {738                byte[] data = GetBytesFromSecureString(secureString);739 740                if (data is not null)741                {742                    try743                    {744                        byte[] encryptedData = _rsaCryptoProvider.EncryptWithSessionKey(data);745                        encryptedDataAsString = Convert.ToBase64String(encryptedData);746                    }747                    finally748                    {749                        Array.Clear(data);750                    }751                }752            }753            else754            {755                throw new PSCryptoException(SecuritySupportStrings.CannotEncryptSecureString);756            }757 758            return encryptedDataAsString;759        }760 761        /// <summary>762        /// Core logic to decrypt a secure string. Assumes session key is already available.763        /// </summary>764        /// <param name="encryptedString">765        /// encrypted string to be decrypted766        /// </param>767        /// <returns></returns>768        protected SecureString DecryptSecureStringCore(string encryptedString)769        {770            // removing an earlier assert from here. It is771            // possible to encrypt and decrypt empty772            // secure strings773            SecureString secureString = null;774 775            // before you can decrypt a key exchange should have776            // happened successfully777            if (_rsaCryptoProvider.CanEncrypt)778            {779                try780                {781                    byte[] data = Convert.FromBase64String(encryptedString);782                    byte[] decryptedData = _rsaCryptoProvider.DecryptWithSessionKey(data);783                    secureString = GetSecureStringFromBytes(decryptedData);784                }785                catch (FormatException)786                {787                    // do nothing788                    // this catch is to ensure that the exception doesn't789                    // go unhandled leading to a crash790                    throw new PSCryptoException();791                }792            }793            else794            {795                Dbg.Assert(false, "Session key not available to decrypt");796            }797 798            return secureString;799        }800 801        #endregion Protected Members802 803        #region Internal Methods804 805        /// <summary>806        /// Encrypt a secure string.807        /// </summary>808        /// <param name="secureString">Secure string to encrypt.</param>809        /// <returns>Encrypted string.</returns>810        /// <remarks>This method zeroes out all interim buffers used</remarks>811        internal abstract string EncryptSecureString(SecureString secureString);812 813        /// <summary>814        /// Decrypt a string and construct a secure string from its815        /// contents.816        /// </summary>817        /// <param name="encryptedString">Encrypted string.</param>818        /// <returns>Secure string object.</returns>819        /// <remarks>This method zeroes out any interim buffers used</remarks>820        internal abstract SecureString DecryptSecureString(string encryptedString);821 822        /// <summary>823        /// Represents the session to be used for requesting public key.824        /// </summary>825        internal abstract RemoteSession Session { get; set; }826 827        /// <summary>828        /// </summary>829        public void Dispose()830        {831            Dispose(true);832            GC.SuppressFinalize(this);833        }834 835        /// <summary>836        /// </summary>837        /// <param name="disposing"></param>838        public void Dispose(bool disposing)839        {840            if (disposing)841            {842                _rsaCryptoProvider?.Dispose();843                _rsaCryptoProvider = null;844 845                _keyExchangeCompleted.Dispose();846            }847        }848 849        /// <summary>850        /// Resets the wait for key exchange.851        /// </summary>852        internal void CompleteKeyExchange()853        {854            _keyExchangeCompleted.Set();855        }856 857        #endregion Internal Methods858    }859 860    /// <summary>861    /// Helper for exchanging keys and encrypting/decrypting862    /// secure strings for serialization in remoting.863    /// </summary>864    internal class PSRemotingCryptoHelperServer : PSRemotingCryptoHelper865    {866        #region Private Members867 868        /// <summary>869        /// This is the instance of runspace pool data structure handler870        /// to use for negotiations.871        /// </summary>872        private RemoteSession _session;873 874        #endregion Private Members875 876        #region Constructors877 878        /// <summary>879        /// Creates the encryption provider, but generates no key.880        /// The key will be imported later.881        /// </summary>882        internal PSRemotingCryptoHelperServer()883        {884            _rsaCryptoProvider = PSRSACryptoServiceProvider.GetRSACryptoServiceProviderForServer();885        }886 887        #endregion Constructors888 889        #region Internal Methods890 891        internal override string EncryptSecureString(SecureString secureString)892        {893            // session!=null check required for DRTs TestEncryptSecureString* entries in CryptoUtilsTest/UTUtils.dll894            bool initiateKeyExchange = true;895 896            if (Session is ServerRemoteSession session)897            {898                Version clientProtocolVersion = session.Context.ClientCapability.ProtocolVersion;899                if (clientProtocolVersion >= RemotingConstants.ProtocolVersion_2_4)900                {901                    // For client v2.4+, we no longer encrypt secure strings, but rely on the underlying secure transport to do the right thing.902                    return ConvertSecureStringToBase64String(secureString);903                }904 905                if (clientProtocolVersion >= RemotingConstants.ProtocolVersion_2_2)906                {907                    // For client v2.2+, server will never initiate key exchange.908                    // For server, just the session key is required to encrypt/decrypt anything909                    initiateKeyExchange = false;910                    _rsaCryptoProvider.GenerateSessionKey();911                }912            }913 914            if (initiateKeyExchange)915            {916                // older clients.917                RunKeyExchangeIfRequired();918            }919 920            return EncryptSecureStringCore(secureString);921        }922 923        internal override SecureString DecryptSecureString(string encryptedString)924        {925            if (Session is ServerRemoteSession session && session.Context.ClientCapability.ProtocolVersion >= RemotingConstants.ProtocolVersion_2_4)926            {927                // For client v2.4+, we no longer encrypt secure strings, but rely on the underlying secure transport to do the right thing.928                return ConvertBase64StringToSecureString(encryptedString);929            }930 931            RunKeyExchangeIfRequired();932 933            return DecryptSecureStringCore(encryptedString);934        }935 936        /// <summary>937        /// Imports a public key from its base64 encoded string representation.938        /// </summary>939        /// <param name="publicKeyAsString">Public key in its string representation.</param>940        /// <returns>True on success.</returns>941        internal bool ImportRemotePublicKey(string publicKeyAsString)942        {943            Dbg.Assert(!string.IsNullOrEmpty(publicKeyAsString), "public key passed in cannot be null");944 945            // generate the crypto provider to use for encryption946            // _rsaCryptoProvider = GenerateCryptoServiceProvider(false);947 948            try949            {950                _rsaCryptoProvider.ImportPublicKeyFromBase64EncodedString(publicKeyAsString);951            }952            catch (PSCryptoException)953            {954                return false;955            }956 957            return true;958        }959 960        /// <summary>961        /// Represents the session to be used for requesting public key.962        /// </summary>963        internal override RemoteSession Session964        {965            get966            {967                return _session;968            }969 970            set971            {972                _session = value;973            }974        }975 976        /// <summary>977        /// </summary>978        /// <param name="encryptedSessionKey"></param>979        /// <returns></returns>980        internal bool ExportEncryptedSessionKey(out string encryptedSessionKey)981        {982            try983            {984                encryptedSessionKey = _rsaCryptoProvider.SafeExportSessionKey();985            }986            catch (PSCryptoException)987            {988                encryptedSessionKey = string.Empty;989                return false;990            }991 992            return true;993        }994 995        /// <summary>996        /// Gets a helper with a test session.997        /// </summary>998        /// <returns>Helper for testing.</returns>999        /// <remarks>To be used only for testing</remarks>1000        internal static PSRemotingCryptoHelperServer GetTestRemotingCryptHelperServer()1001        {1002            PSRemotingCryptoHelperServer helper = new PSRemotingCryptoHelperServer();1003            helper.Session = new TestHelperSession();1004 1005            return helper;1006        }1007 1008        #endregion Internal Methods1009    }1010 1011    /// <summary>1012    /// Helper for exchanging keys and encrypting/decrypting1013    /// secure strings for serialization in remoting.1014    /// </summary>1015    internal class PSRemotingCryptoHelperClient : PSRemotingCryptoHelper1016    {1017        #region Private Members1018 1019        #endregion Private Members1020 1021        #region Constructors1022 1023        /// <summary>1024        /// Creates the encryption provider, but generates no key.1025        /// The key will be imported later.1026        /// </summary>1027        internal PSRemotingCryptoHelperClient()1028        {1029            _rsaCryptoProvider = PSRSACryptoServiceProvider.GetRSACryptoServiceProviderForClient();1030        }1031 1032        #endregion Constructors1033 1034        #region Protected Methods1035 1036        #endregion Protected Methods1037 1038        #region Internal Methods1039 1040        internal override string EncryptSecureString(SecureString secureString)1041        {1042            if (Session is ClientRemoteSession session && session.ServerProtocolVersion >= RemotingConstants.ProtocolVersion_2_4)1043            {1044                // For server v2.4+, we no longer encrypt secure strings, but rely on the underlying secure transport to do the right thing.1045                return ConvertSecureStringToBase64String(secureString);1046            }1047 1048            RunKeyExchangeIfRequired();1049 1050            return EncryptSecureStringCore(secureString);1051        }1052 1053        internal override SecureString DecryptSecureString(string encryptedString)1054        {1055            if (Session is ClientRemoteSession session && session.ServerProtocolVersion >= RemotingConstants.ProtocolVersion_2_4)1056            {1057                // For server v2.4+, we no longer encrypt secure strings, but rely on the underlying secure transport to do the right thing.1058                return ConvertBase64StringToSecureString(encryptedString);1059            }1060 1061            RunKeyExchangeIfRequired();1062 1063            return DecryptSecureStringCore(encryptedString);1064        }1065 1066        /// <summary>1067        /// Export the public key as a base64 encoded string.1068        /// </summary>1069        /// <param name="publicKeyAsString">on execution will contain1070        /// the public key as string</param>1071        /// <returns>True on success.</returns>1072        internal bool ExportLocalPublicKey(out string publicKeyAsString)1073        {1074            // generate keys - the method already takes of creating1075            // only when its not already created1076 1077            try1078            {1079                _rsaCryptoProvider.GenerateKeyPair();1080            }1081            catch (PSCryptoException)1082            {1083                throw;1084 1085                // the caller has to ensure that they1086                // complete the key exchange process1087            }1088 1089            try1090            {1091                publicKeyAsString = _rsaCryptoProvider.GetPublicKeyAsBase64EncodedString();1092            }1093            catch (PSCryptoException)1094            {1095                publicKeyAsString = string.Empty;1096                return false;1097            }1098 1099            return true;1100        }1101 1102        /// <summary>1103        /// </summary>1104        /// <param name="encryptedSessionKey"></param>1105        /// <returns></returns>1106        internal bool ImportEncryptedSessionKey(string encryptedSessionKey)1107        {1108            Dbg.Assert(!string.IsNullOrEmpty(encryptedSessionKey), "encrypted session key passed in cannot be null");1109 1110            try1111            {1112                _rsaCryptoProvider.ImportSessionKeyFromBase64EncodedString(encryptedSessionKey);1113            }1114            catch (PSCryptoException)1115            {1116                return false;1117            }1118 1119            return true;1120        }1121 1122        /// <summary>1123        /// Represents the session to be used for requesting public key.1124        /// </summary>1125        internal override RemoteSession Session { get; set; }1126 1127        /// <summary>1128        /// Gets a helper with a test session.1129        /// </summary>1130        /// <returns>Helper for testing.</returns>1131        /// <remarks>To be used only for testing</remarks>1132        internal static PSRemotingCryptoHelperClient GetTestRemotingCryptHelperClient()1133        {1134            PSRemotingCryptoHelperClient helper = new PSRemotingCryptoHelperClient();1135            helper.Session = new TestHelperSession();1136 1137            return helper;1138        }1139 1140        #endregion Internal Methods1141    }1142 1143    #region TestHelpers1144 1145    internal class TestHelperSession : RemoteSession1146    {1147        internal override void StartKeyExchange()1148        {1149            // intentionally left blank1150        }1151 1152        internal override RemotingDestination MySelf1153        {1154            get1155            {1156                return RemotingDestination.InvalidDestination;1157            }1158        }1159 1160        internal override void CompleteKeyExchange()1161        {1162            // intentionally left blank1163        }1164    }1165    #endregion TestHelpers1166}1167