Samstag, 19. Oktober 2013

Geheimnis in Applikation speichern .NET



Manchmal möchte man ein Geheimnis in der Applikation speichern. Nun muss aber die Applikation die Möglichkeit besitzen das Geheimnis zu lesen.Und genau das ist der Knackpunkt.

Wenn irgendwo so was steht

const string password = "CP3wCUrRJhEd5iXahgq4Qpz3";
kann man davon ausgehen, dass jeder Idiot dieses Password auslesen kann. Z.b. mit dotPeek https://www.jetbrains.com/decompiler/


Ich dachte mir, ich versuche mich mal an einer Lösung welche den Key Store vom Betriebssystem nutzt.




Test First



[TestMethod]
public void EncryptDecrypt()
{
    var input = GetData();
    var identifier = Guid.NewGuid().ToString();
    var container = CredentialStorage.CredentialStorage.Encrypt(identifier, input);
    var decrypted = CredentialStorage.CredentialStorage.Decrypt(identifier, container);
    CredentialStorage.CredentialStorage.RSADeleteKeyInCSP(identifier);


    for (int i = 0; i < decrypted.Length; i++)
    {
        if (decrypted[i] != input[i])
        {
            Assert.Fail("Input and output are not equal.");
        }
    }
}

Byte[] input hält nur ein Haufen voller zufälligen Bytes.
string identifier soll im Produktiv-System ein eindeutiger Name sein.

Nach dem verschlüsseln entschlüsseln wir die Daten und hoffen auf gleiche Daten.

Verschlüsseln

Wir holen uns einen RsaCryptoServiceProvider (siehe unten "CSP Key Storage Helper") und verschlüsseln damit einen AES Schlüssel. Mit diesen AES Schlüssel verschlüsseln wir die Datem und geben diesen zurück.


public static StorageContainer Encrypt(string identifier, byte[] data)
{
    var provider = GetRsaCryptoServiceProvider(identifier);

    byte[] encryptedData;
    byte[] encryptedKeywithRsa;
    byte[] iv;
    // Create an RijndaelManaged object 
    // with the specified key and IV. 
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.KeySize = 256;

        rijAlg.GenerateKey();
        rijAlg.GenerateIV();

        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(data, 0, data.Length);
                csEncrypt.FlushFinalBlock();
            }
            encryptedData = msEncrypt.ToArray();
        }

        encryptedKeywithRsa = provider.Encrypt(rijAlg.Key, true);
        iv = rijAlg.IV;
    }

    var container = new StorageContainer {Iv = iv, EncryptedKeywithRsa = encryptedKeywithRsa, EncryptedData = encryptedData};

    return container;
}


Entschlüsseln

So trivial wie das verschlüssseln, nur dass geprüft wird ob im Windows Key Storage der Schlüssel mit diesen Identifier vorhanden ist.


public static byte[] Decrypt(string identifier, StorageContainer container)
{
    if (!DoesKeyExists(identifier))
    {
        throw new Exception(string.Format("No Key in CSP Container \"{0}\" ", identifier));
    }


    var provider = GetRsaCryptoServiceProvider(identifier);

    byte[] encryptedData;
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.KeySize = 256;

        rijAlg.Key = provider.Decrypt(container.EncryptedKeywithRsa, true);
        rijAlg.IV = container.Iv;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(container.EncryptedData, 0, container.EncryptedData.Length);
            }
            encryptedData = msEncrypt.ToArray();
        }
    }

    return encryptedData;
}

CSP Key Storage Helper




private static RSACryptoServiceProvider GetRsaCryptoServiceProvider(string identifier)
{
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = identifier;

    var provider = new RSACryptoServiceProvider(cspParams);
    provider.PersistKeyInCsp = true;
    provider.KeySize = 4096;
    return provider;
}


public static bool DoesKeyExists(string containerName)
{
	var cspParams = new CspParameters
	{
		Flags = CspProviderFlags.UseExistingKey,
		KeyContainerName = containerName
	};

	try
	{
		var provider = new RSACryptoServiceProvider(cspParams);
	}
	catch (Exception e)
	{
		return false;
	}
	return true;
}


public static void RSADeleteKeyInCSP(string ContainerName)
{
	// Create a new instance of CspParameters.  Pass
	// 13 to specify a DSA container or 1 to specify
	// an RSA container.  The default is 1.
	CspParameters cspParams = new CspParameters
	{
		KeyContainerName = ContainerName
	};

	// Specify the container name using the passed variable.

	//Create a new instance of RSACryptoServiceProvider. 
	//Pass the CspParameters class to use the 
	//key in the container.
	RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(cspParams);

	//Explicitly set the PersistKeyInCsp property to false
	//to delete the key entry in the container.
	RSAalg.PersistKeyInCsp = false;

	//Call Clear to release resources and delete the key from the container.
	RSAalg.Clear();

	//Indicate that the key was persisted.
	Console.WriteLine("The RSA key was deleted from the container, \"{0}\".", ContainerName);
}

Download

CredentialStoragePublic.7z https://db.tt/NDFhKbgG

Fazit

Ich bin ein Feind von Security through obscurity, aber ich glaube dass ich mit dieser Art von Verschlüsselung zumindest ein paar Use Cases gut bedienen kann. Es gibt noch die Möglichkeit, dass der Benutzer beim Zugriff aus den Key Storage seine Windows Credentials angeben muss. Um das zu realisieren müssen nur die CSP Parameter entsprechend anspassen.

Interessante Links

How Private Keys Are Stored
http://technet.microsoft.com/en-us/library/cc962112.aspx
Key Storage and Retrieval
http://msdn.microsoft.com/en-us/library/windows/desktop/bb204778%28v=vs.85%29.aspx

Keine Kommentare:

Kommentar veröffentlichen

Über mich