Dpapi | Engin Polat\'ın Windows 8 , Windows Phone 8 ve C# içerikli programcılık sitesi

Arşiv

Etiketlenen yazılar dpapi

String ve SecureString için Encrypt, Decrypt ExtensionMethod

04 March 2013 Yorum yapılmamış

Windows Data Protection API (DPAPI) encryption key’e ihtiyaç duymadan, kullanıcının oturumuna özel şifreleme yapabilen bir yapıdır.

DPAPI’yi kullanabilmek için System.Security referans’ının proje’ye eklenmesi gerekmektedir.

.Net 2.0 versiyonundan beri .Net Framework‘un bir parçası olan DPAPI sayesinde metin şifreleme ve şifre çözme çok kolaylaşmıştır.

byte[] tipinde yapıların şifrelenmesi Protect() method’u ile, şifre çözülmesi ise Unprotect() method’u ile yapılmaktadır;

public static byte[] Encrypt(byte[] data)
{
	return ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
}

public static byte[] Decrypt(byte[] data)
{
	return ProtectedData.Unprotect(data, null, DataProtectionScope.CurrentUser);
}

Bu yapının string tipindeki değişkenler ile de kullanılabilmesi için aşağıdaki extension method‘lar kullanılabilir;

public static class ExtensionMethods
{
	private const DataProtectionScope Scope = DataProtectionScope.CurrentUser;

	public static string Encrypt(this string plainText)
	{
		if (plainText == null) throw new ArgumentNullException("plainText");

		var data = Encoding.Unicode.GetBytes(plainText);
		byte[] encrypted = ProtectedData.Protect(data, null, Scope);

		return Convert.ToBase64String(encrypted);
	}

	public static string Decrypt(this string encryptedText)
	{
		if (encryptedText == null) throw new ArgumentNullException("encryptedText");

		byte[] data = Convert.FromBase64String(encryptedText);
		byte[] decrypted = ProtectedData.Unprotect(data, null, Scope);

		return Encoding.Unicode.GetString(decrypted);
	}
}

ProtectedData sınıfının Protect() ve Unprotect() method’larının üçüncü parametresi DataProtectionScope enum tipindedir ve CurrentUser, LocalMachine değerlerinden birini alabilir.

Artık string tipindeki değişkenleri Encrypt() ve Decrypt() method’ları ile şifreleyebilir, şifrelerini çözebiliriz. Fakat bu değişkenlerin değerleri hafızada hala okunabilir formatta tutuluyor olacak.

Eğer değişkenlerin içeriğinin hafızada daha güvenli tutulmasını istiyorsak SecureString tipini kullanmalıyız.

(MSDN’de yeralan Making Strings More Secure makalesini okumanızı tavsiye ederim.)

SecureString tipi için de Encrypt() ve Decrypt() extension method‘ları yazalım;

public static string Encrypt(this SecureString value)
{
	if (value == null) throw new ArgumentNullException("value");

	IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(value);
	try
	{
		char[] buffer = new char[value.Length];
		Marshal.Copy(ptr, buffer, 0, value.Length);

		byte[] data = Encoding.Unicode.GetBytes(buffer);
		byte[] encrypted = ProtectedData.Protect(data, null, Scope);

		return Convert.ToBase64String(encrypted);
	}
	finally
	{
		Marshal.ZeroFreeCoTaskMemUnicode(ptr);
	}
}

public static SecureString DecryptSecure(this string encryptedText)
{
	if (encryptedText == null) throw new ArgumentNullException("encryptedText");

	byte[] data = Convert.FromBase64String(encryptedText);
	byte[] decrypted = ProtectedData.Unprotect(data, null, Scope);

	SecureString ss = new SecureString();

	int count = Encoding.Unicode.GetCharCount(decrypted);
	int bc = decrypted.Length / count;
	for (int i = 0; i < count; i++)
	{
		ss.AppendChar(Encoding.Unicode.GetChars(decrypted, i * bc, bc)[0]);
	}

	ss.MakeReadOnly();
	return ss;
}

Son olarak string tipi ile SecureString tipi arasındaki dönüşüm extension method'larını da yazalım;

public static SecureString ToSecureString(this IEnumerable value)
{
	if (value == null) throw new ArgumentNullException("value");

	var secured = new SecureString();

	var charArray = value.ToArray();
	for (int i = 0; i < charArray.Length; i++)
	{
		secured.AppendChar(charArray[i]);
	}

	secured.MakeReadOnly();
	return secured;
}

public static string UnWrap(this SecureString value)
{
	if (value == null) throw new ArgumentNullException("value");

	IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(value);
	try
	{
		return Marshal.PtrToStringUni(ptr);
	}
	finally
	{
		Marshal.ZeroFreeCoTaskMemUnicode(ptr);
	}
}

Artık string ve SecureString tipleri için hazırladığımız extension method'ları kullanabiliriz;

bool isOk = false;

string plain = "password";

string encrypted = plain.Encrypt();

string decrypted = encrypted.Decrypt();

SecureString plainSecure = plain.ToSecureString();

string encryptedSecure = plainSecure.Encrypt();

string decryptedSecure = encryptedSecure.Decrypt();

isOk = plain == decrypted;
isOk = plain == plainSecure.UnWrap();
isOk = plain == decryptedSecure;