Библиотеке DotNetOpenAuth OAuth 2 требуются параметры RSAParameters для доступа к открытым и закрытым ключам (пример в DotNetOpenAuth OAuth 2 - UriStyleMessageFormatter, который использует RSAParameters для создания RSACryptoServiceProvider).
Я наткнулся на Белая книга безопасности Azure1004 *, в котором указано, что Azure устанавливает сертификаты в «хранилище сертификатов с флагом, указывающим, что закрытый ключ можно использовать, но не экспортировать».Что, я считаю, может быть в основе этой проблемы.
Хотя я смог извлечь открытый и закрытый ключи из сертификата при разработке и отладке локально, ссылаясь на сертификат по его отпечатку (пример ниже), у меня естьне удалось получить тот же код, работающий в Azure.
Следующий код выдает ошибку: «Ключ недопустим для использования в указанном состоянии» в Azure
public class Global : System.Web.HttpApplication, IContainerAccessor
{
private static string thumbPrint = "<<my certificate thumbprint>>";
public static readonly RSAParameters AuthorizationServerSigningPublicKey = OAuthUtil.GetPublicKey(thumbPrint);
internal static readonly RSAParameters ResourceServerEncryptionPrivateKey = OAuthUtil.GetPrivateKey(thumbPrint);
//....... unnecessary code omitted ..... //
public static class OAuthUtil
{
public static RSAParameters GetPublicKey(string thumbPrint)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var cert = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, true)[0];
var rsaParams = ((RSACryptoServiceProvider) cert.PublicKey.Key).ExportParameters(false);
return rsaParams;
}
public static RSAParameters GetPrivateKey(string thumbPrint)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var cert = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, true)[0];
var rsaParams = ((RSACryptoServiceProvider) cert.PrivateKey).ExportParameters(true);
return rsaParams;
}
}
Код шифрования / дешифрования вAzure, основанный на том же сертификате (пример ниже), который не требует экспорта ключа, работает нормально:
public class Certificate
{
public string FriendlyName { get; set; }
public string IssuedBy { get; set; }
public string IssuedTo { get; set; }
public string ExpirationDate { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
}
public ActionResult Keys()
{
X509Certificate2Collection selectedCerts = new X509Certificate2Collection();
var certList = new List<Certificate>();
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
foreach (X509Certificate2 cert in store.Certificates)
{
// Encrypt string "hello world"
CspParameters CSPParam = new CspParameters();
CSPParam.Flags = CspProviderFlags.UseMachineKeyStore;
string PlainString = "hello world";
byte[] cipherbytes = ASCIIEncoding.ASCII.GetBytes(PlainString);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] cipher = rsa.Encrypt(cipherbytes, false);
var encryptedString = Convert.ToBase64String(cipher);
var cert2 = cert;
string decryptedString = "verify = " + cert2.Verify() ;
if (cert2.HasPrivateKey && cert2.Verify())
{
// Decrypt encrypted string..
RSACryptoServiceProvider rsaDecrypt = (RSACryptoServiceProvider)cert2.PrivateKey;
byte[] cipherbytes2 = Convert.FromBase64String(encryptedString);
byte[] plainbytes = rsaDecrypt.Decrypt(cipherbytes2, false);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
decryptedString = enc.GetString(plainbytes);
}
var certItem = new Certificate
{
FriendlyName = cert.FriendlyName,
IssuedBy = cert.Issuer,
IssuedTo = cert.SubjectName.Name,
ExpirationDate = cert.NotAfter.ToString("d"),
PublicKey =
"Public Key: " + cert.GetPublicKeyString() + "<br/>Encrypted String: " + encryptedString + "<br/>Decrypted String: " +
decryptedString,
PrivateKey =
"cert has private key?: " + cert.HasPrivateKey + "<br/> key algo:" +
cert.GetKeyAlgorithm()
};
certList.Add(certItem);
}
}
finally
{
store.Close();
}
return View(certList);
}
Кроме переписывания библиотеки OAuth 2 для использования ссылок RSACryptoServiceProvider вместо RSAParameters, есть какой-то способ, которым я мог бызаставить это работать в Azure?
Кто-нибудь еще испытывает ту же проблему с DotNetOpenAuth OAuth 2 и Azure при чтении сертификатов из хранилища?
Я бы хотел избежать таких хаков, как установкасертификат с правами экспорта с использованием задачи запуска (из соображений безопасности).