How can I use ConvertTo-SecureString

I know this is an old post. I am posting this for completeness and posterity, because I couldn't find a complete answer on MSDN or stackoverflow. It will be here in case I ever need to do this again.

It is a C# implementation of of powershell's ConvertTo-SecureString with AES encryption (turned on by using the -key option). I will leave it for exercise to code a C# implementation of ConvertFrom-SecureString.

# forward direction
[securestring] $someSecureString = read-host -assecurestring
[string] $psProtectedString = ConvertFrom-SecureString -key (1..16) -SecureString $someSecureString
# reverse direction
$back = ConvertTo-SecureString -string $psProtectedString -key (1..16)

My work is combining answers and re-arranging user2748365's answer to be more readable and adding educational comments! I also fixed the issue with taking a substring -- at the time of this post, his code only has two elements in strArray.

using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Globalization;

// psProtectedString - this is the output from
//   powershell> $psProtectedString = ConvertFrom-SecureString -SecureString $aSecureString -key (1..16)
// key - make sure you add size checking 
// notes: this will throw an cryptographic invalid padding exception if it cannot decrypt correctly (wrong key)
public static SecureString ConvertToSecureString(string psProtectedString, byte[] key)
{
    // '|' is indeed the separater
    byte[] asBytes = Convert.FromBase64String( psProtectedString );
    string[] strArray = Encoding.Unicode.GetString(asBytes).Split(new[] { '|' });

    if (strArray.Length != 3) throw new InvalidDataException("input had incorrect format");

    // strArray[0] is a static/magic header or signature (different passwords produce
    //    the same header)  It unused in our case, looks like 16 bytes as hex-string
    // you know strArray[1] is a base64 string by the '=' at the end
    //    the IV is shorter than the body, and you can verify that it is the IV, 
    //    because it is exactly 16bytes=128bits and it decrypts the password correctly
    // you know strArray[2] is a hex-string because it is [0-9a-f]
    byte[] magicHeader = HexStringToByteArray(encrypted.Substring(0, 32));
    byte[] rgbIV = Convert.FromBase64String(strArray[1]);
    byte[] cipherBytes = HexStringToByteArray(strArray[2]);

    // setup the decrypter
    SecureString str = new SecureString();
    SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create();
    ICryptoTransform transform = algorithm.CreateDecryptor(key, rgbIV);
    using (var stream = new CryptoStream(new MemoryStream(cipherBytes), transform, CryptoStreamMode.Read))
    {
        // using this silly loop format to loop one char at a time
        // so we never store the entire password naked in memory
        int numRed = 0;
        byte[] buffer = new byte[2]; // two bytes per unicode char
        while( (numRed = stream.Read(buffer, 0, buffer.Length)) > 0 )
        {
            str.AppendChar(Encoding.Unicode.GetString(buffer).ToCharArray()[0]);
        }
    }

    //
    // non-production code
    // recover the SecureString; just to check
    // from http://stackoverflow.com/questions/818704/how-to-convert-securestring-to-system-string
    //
    IntPtr valuePtr = IntPtr.Zero;
    string secureStringValue = "";
    try
    {
        // get the string back
        valuePtr = Marshal.SecureStringToGlobalAllocUnicode(str);
        secureStringValue = Marshal.PtrToStringUni(valuePtr);
    }
    finally
    {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
    }

    return str;
}
// from http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
public static byte[] HexStringToByteArray(String hex)
{
    int NumberChars = hex.Length;
    byte[] bytes = new byte[NumberChars / 2];
    for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);

    return bytes;
}
public static SecureString DecryptPassword( string psPasswordFile, byte[] key )
{
    if( ! File.Exists(psPasswordFile)) throw new ArgumentException("file does not exist: " + psPasswordFile);

    string formattedCipherText = File.ReadAllText( psPasswordFile );

    return ConvertToSecureString(formattedCipherText, key);
}

According to the docs on ConvertFrom-SecureString the AES encryption algorithm is used:

If an encryption key is specified by using the Key or SecureKey parameters, the Advanced Encryption Standard (AES) encryption algorithm is used. The specified key must have a length of 128, 192, or 256 bits because those are the key lengths supported by the AES encryption algorithm. If no key is specified, the Windows Data Protection API (DPAPI) is used to encrypt the standard string representation.

Look at the DecryptStringFromBytes_Aes example in the MSDN docs.

BTW an easy option would be to use the PowerShell engine from C# to execute the ConvertTo-SecureString cmdlet to do the work. Otherwise, it looks like the initialization vector is embedded somewhere in the ConvertFrom-SecureString output and may or may not be easy to extract.