BouncyCastle RSAPrivateKey для .NET RSAPrivateKey
Я создаю систему распространения сертификатов, чтобы отслеживать клиентов и прочее.
Что происходит:
- Клиент отправляет CSR на сервер
- Сервер проверяет и подписывает сертификат
- Сервер отправляет подписанный сертификат клиенту
- Клиент ставит подписанный сертификат плюс закрытый ключ в хранилище Windows.
Итак, на клиенте это происходит:
//Pseudo Server Object:
Server s = new Server();
//Requested Certificate Name and things
X509Name name = new X509Name("CN=Client Cert, C=NL");
//Key generation 2048bits
RsaKeyPairGenerator rkpg = new RsaKeyPairGenerator();
rkpg.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
AsymmetricCipherKeyPair ackp = rkpg.GenerateKeyPair();
//PKCS #10 Certificate Signing Request
Pkcs10CertificationRequest csr = new Pkcs10CertificationRequest("SHA1WITHRSA", name, ackp.Public, null, ackp.Private);
//Make it a nice PEM thingie
StringBuilder sb = new StringBuilder();
PemWriter pemwrit = new PemWriter(new StringWriter(b));
pemwrit.WriteObject(csr);
pemwrit.Writer.Flush();
s.SendRequest(sb.ToSting());
Хорошо Итак, я пропущу сервер. Просто верьте мне, что сервер подписывает сертификат и отправляет его клиенту. Вот где я возьму действие.
PemReader pr = new PemReader(new StringReader(b.ToString()));
X509Certificate cert = (X509Certificate)pr.ReadObject();
//So lets asume I saved the AsymmetricCipherKeyPair (ackp) from before
//I have now the certificate and my private key;
//first I make it a "Microsoft" x509cert.
//This however does not have a PrivateKey thats in the AsymmetricCipherKeyPair (ackp)
System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);
//So here comes the RSACryptoServerProvider:
System.Security.Cryptography.RSACryptoServiceProvider rcsp = new System.Security.Cryptography.RSACryptoServiceProvider();
//And the privateKeyParameters
System.Security.Cryptography.RSAParameters parms = new System.Security.Cryptography.RSAParameters();
//now I have to translate ackp.PrivateKey to parms;
RsaPrivateCrtKeyParameters BCKeyParms = ((RsaPrivateCrtKeyParameters)ackp1.Private);
//D is the private exponent
parms.Modulus = BCKeyParms.Modulus.ToByteArray();
parms.P = BCKeyParms.P.ToByteArray();
parms.Q = BCKeyParms.Q.ToByteArray();
parms.DP = BCKeyParms.DP.ToByteArray();
parms.DQ = BCKeyParms.DQ.ToByteArray();
parms.InverseQ = BCKeyParms.QInv.ToByteArray();
parms.D = BCKeyParms.Exponent.ToByteArray();
parms.Exponent = BCKeyParms.PublicExponent.ToByteArray();
//Now I should be able to import the RSAParameters into the RSACryptoServiceProvider
rcsp.ImportParameters(parms);
//<em><b>not really</b></em> This breaks says "Bad Data" and not much more. I'll Post the
//stacktrace at the end
//I open up the windows cert store because thats where I want to save it.
//Add it and save it this works fine without the privkey.
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed);
store.Add(netcert);
store.Close();
Теперь вы, вероятно, думаете, что на стороне сервера должно быть что-то не так. Ну, это то, что я думал тоже, но когда я сделал pfx файл из этого сертификата и импортировал его вручную, он работал нормально....
Каким-то образом существует разница между частным ключом .NET RSA и частным ключом BouncyCastle RSA, и я не могу на него наложить.
Вы, вероятно, предложите импортировать pfx, а затем получите закрытый ключ от него через X509Store. Я пытался.: S И не удалось. Как только я попытаюсь ExportParameters(true)
, истина означает включение частных параметров. Он говорит: "Ключ недействителен для использования в указанном состоянии". См. Полное исключение в конце.
Я надеюсь, что некоторые из вас убили эту свинью раньше или могли бы мне помочь.
***Exceptions:***
System.Security.Cryptography.CryptographicException was unhandled
Message="Key not valid for use in specified state.\r\n"
Source="mscorlib"
StackTrace:
at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.Utils._ExportKey(SafeKeyHandle hKey, Int32 blobType, Object cspObject)
at System.Security.Cryptography.RSACryptoServiceProvider.ExportParameters(Boolean includePrivateParameters)
InnerException:
***And the other one:***
System.Security.Cryptography.CryptographicException was unhandled
Message="Bad Data.\r\n"
Source="mscorlib"
StackTrace:
at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.Utils._ImportKey(SafeProvHandle hCSP, Int32 keyNumber, CspProviderFlags flags, Object cspObject, SafeKeyHandle& hKey)
at System.Security.Cryptography.RSACryptoServiceProvider.ImportParameters(RSAParameters parameters)
InnerException:
Ответы
Ответ 1
Ответ (от имени пользователя) указывает на правильное направление: дополнение.
Последняя версия Bouncy-castle от git имеет следующий код:
public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
rp.P = privKey.P.ToByteArrayUnsigned();
rp.Q = privKey.Q.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
return rp;
}
private static byte[] ConvertRSAParametersField(BigInteger n, int size)
{
byte[] bs = n.ToByteArrayUnsigned();
if (bs.Length == size)
return bs;
if (bs.Length > size)
throw new ArgumentException("Specified size too small", "size");
byte[] padded = new byte[size];
Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
return padded;
}
nb: Этот код не в версии nuget (2011) из bouncy castle, или в большинстве образцов кода были просто скопированы параметры RSA.
Этот код отличается от кода, который вы можете видеть где-либо еще, который в основном копирует/вставляет ключевые параметры и не выполняет дополнительный шаг заполнения.
Ответ 2
FYI, я добавил эту функциональность в класс Org.BouncyCastle.Security.DotNetUtilities; он скоро появится в релизе 1.6.
Ответ 3
Я нашел его!
Или по крайней мере часть его:)
Что касается PrivateKey.ExportToParameters(true)
Все еще не работает, но у этого есть кое-что, что с ключом было 2048 бит. Потому что, когда я изменил его на 1024bit, это сработало. Поэтому, если кто-нибудь узнает, зачем мне держать меня в курсе.
Итак, идем снова.
//BouncyCastle Key objects
RsaPrivateCrtKeyParameters rpckp = ((RsaPrivateCrtKeyParameters)ackp.Private);
//.NET RSA Key objects
System.Security.Cryptography.RSACryptoServiceProvider rcsp = new System.Security.Cryptography.RSACryptoServiceProvider();
System.Security.Cryptography.RSAParameters parms = new System.Security.Cryptography.RSAParameters();
//So the thing changed is offcourse the ToByteArrayUnsigned() instead of
//ToByteArray()
parms.Modulus = rpckp.Modulus.ToByteArrayUnsigned();
parms.P = rpckp.P.ToByteArrayUnsigned();
parms.Q = rpckp.Q.ToByteArrayUnsigned();
parms.DP = rpckp.DP.ToByteArrayUnsigned();
parms.DQ = rpckp.DQ.ToByteArrayUnsigned();
parms.InverseQ = rpckp.QInv.ToByteArrayUnsigned();
parms.D = rpckp.Exponent.ToByteArrayUnsigned();
parms.Exponent = rpckp.PublicExponent.ToByteArrayUnsigned();
//So now this now appears to work.
rcsp.ImportParameters(parms);
Итак, теперь я могу добавить полный сертификат в свой магазин:)
Ответ 4
Я думаю, что нашел решение этой проблемы. Он не имеет ничего общего с ключом per, а скорее с объектом X509Certificate2, который должен быть создан с флагом X509KeyStorageFlags.Exportable.
В этом случае ваш X509Certificate2 был создан этим методом:
System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);
Поэтому убедитесь, что вы передаете флаг exportable в конструкторе X509Certificate2 в этом методе. В моей ситуации мне нужно было подписать некоторые данные с помощью закрытого ключа, расположенного в файле PFX, поэтому мне пришлось написать это:
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags);
Теперь я могу сделать
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);
НТН,
Стефан
Ответ 5
Ни один из решений не работал у меня. Но я заметил, что исключение всегда вызывается, когда один из следующих массивов:
parms.Modulus = rpckp.Modulus.ToByteArrayUnsigned();
parms.P = rpckp.P.ToByteArrayUnsigned();
parms.Q = rpckp.Q.ToByteArrayUnsigned();
parms.DP = rpckp.DP.ToByteArrayUnsigned();
parms.DQ = rpckp.DQ.ToByteArrayUnsigned();
parms.InverseQ = rpckp.QInv.ToByteArrayUnsigned();
parms.D = rpckp.Exponent.ToByteArrayUnsigned();
parms.Exponent = rpckp.PublicExponent.ToByteArrayUnsigned();
имеет другой размер, чем его сосед:
DP, DQ, InverseQ, P, Q
или двойной размер:
D, Modulus
Для каждой из этих двух групп я рассчитал максимальную длину и добавил дополнительные нули в начале каждого массива, чтобы сделать их одинаковой длины (одинаковой для каждой группы).
Это работает, я полагаю, что ImportParameters
проверяет, что они имеют одинаковую длину (к сожалению, у меня нет доступа к коду ImportParameters
, кажется, что он вызывает некоторую родную библиотеку).
Я использую BouncyCastle.Crypto.dll ver 1.7