CryptographicException „Key not valid for use in specified state.” (Cheia nu este valabilă pentru utilizare în starea specificată) în timp ce se încearcă să se exporte RSAParameters (Parametrii RSAP) ai unei chei private X509 (Programare, C#,.Net, Criptare, Criptografie, Rsa)

kalrashi a intrebat.

Mă holbez la acest lucru de ceva timp și datorită lui documentația MSDN nu pot să-mi dau seama cu adevărat ce se întâmplă. Practic, încarc un fișier PFX de pe disc într-un fișier X509Certificate2 și încerc să criptez un șir de caractere folosind cheia publică și să decriptez folosind cheia privată.

De ce sunt nedumerit: criptarea/decriptarea funcționează atunci când trec referința la fișierul RSACryptoServiceProvider însăși:

byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider);
string foo1 = DecryptRSA(ed1, x.PrivateKey as RSACryptoServiceProvider);

Dar dacă exportul și trec în jurul RSAParameter:

byte[] ed = EncryptRSA("foo", (x.PublicKey.Key as RSACryptoServiceProvider).ExportParameters(false));
string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true));

…se aruncă o excepție „Cheia nu este valabilă pentru utilizare în starea specificată.” în timp ce încerc să export cheia privată către RSAParameter. Vă rugăm să rețineți că certificatul din care este generat PFX este marcat ca fiind exportabil (adică am folosit stegulețul pe în timpul creării certificatului). Aveți vreo idee despre ce cauzează excepția?

static void Main(string[] args)
{
    X509Certificate2 x = new X509Certificate2(@"C:tempcerts1test.pfx", "test");
    x.FriendlyName = "My test Cert";
    
    X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadWrite);
    try
    {
        store.Add(x);
    }
    finally
    {
        store.Close();
    }

    byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider);
    string foo1 = DecryptRSA(ed1, x.PrivateKey as RSACryptoServiceProvider);

    byte[] ed = EncryptRSA("foo", (x.PublicKey.Key as RSACryptoServiceProvider).ExportParameters(false));
    string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true));
}

private static byte[] EncryptRSA(string data, RSAParameters rsaParameters)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();
    byte[] plainData = bytConvertor.GetBytes(data);

    RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
    publicKey.ImportParameters(rsaParameters);
    return publicKey.Encrypt(plainData, true);
}

private static string DecryptRSA(byte[] data, RSAParameters rsaParameters)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();

    RSACryptoServiceProvider privateKey = new RSACryptoServiceProvider();
    privateKey.ImportParameters(rsaParameters);

    byte[] deData = privateKey.Decrypt(data, true);
    return bytConvertor.GetString(deData);
}

private static byte[] EncryptRSA(string data, RSACryptoServiceProvider publicKey)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();
    byte[] plainData = bytConvertor.GetBytes(data);

    return publicKey.Encrypt(plainData, true);
}

private static string DecryptRSA(byte[] data, RSACryptoServiceProvider privateKey)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();

    byte[] deData = privateKey.Decrypt(data, true);
    return bytConvertor.GetString(deData);
}

Doar pentru a clarifica în codul de mai sus, partea în bold este aruncat:string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider)**.ExportParameters(true)**);

Comentarii

  • În limba germană, mesajul de eroare sună astfel: „Schlüssel ist im angegebenen Status nicht gültig.„. –  > Por Uwe Keim.
7 răspunsuri
Iridium

Cred că problema ar putea fi că cheia nu este marcată ca fiind exportabilă. Există un alt constructor pentru X509Certificate2 care ia un enum X509KeyStorageFlags. Încercați să înlocuiți linia:

X509Certificate2 x = new X509Certificate2(@"C:tempcerts1test.pfx", "test");

Cu aceasta:

X509Certificate2 x = new X509Certificate2(@"C:tempcerts1test.pfx", "test", X509KeyStorageFlags.Exportable);

Comentarii

  • Mulțumesc! M-ați salvat de o mulțime de probleme! 🙂 –  > Por conciliator.
  • 8 ani mai târziu, asta mi-a salvat nebunia! Vă mulțumesc din suflet. –  > Por arvind.d.
  • Care sunt implicațiile negative/grozavii de la setarea acestui indicator exportabil? –  > Por ElFik.
  • @ElFik – Deoarece cheia privată este utilizată pentru decriptare și semnare, este important ca aceasta să fie păstrată în siguranță. Atunci când este setat steagul Exportabil, cheia privată poate fi salvată într-un fișier și copiată în altă parte și, deși acest lucru este bun dacă doriți să puteți, de exemplu, să copiați cheia pe o altă mașină etc., reduce oarecum securitatea, deoarece poate fi, de asemenea, extrasă mai ușor de către o terță parte neautorizată (de exemplu, dacă vă lăsați PC-ul conectat și deblocat). Acest lucru nu înseamnă că, cu indicatorul Exportable nu se poate accesa setat, că este imposibil de a extrage cheia, dar cu siguranță face ca acest lucru să fie mai dificil. –  > Por Iridium.
  • Am înțeles, deci îndrumările ar putea fi rezumate la „folosiți exportabilul doar dacă aveți nevoie, dar dacă aveți nevoie, nu ezitați să îl folosiți”? –  > Por ElFik.
va webster

Pentru problema pe care am întâlnit-o, o schimbare de cod nu era o opțiune, deoarece aceeași bibliotecă era instalată și funcționa în altă parte.

Răspunsul lui Iridium m-a determinat să caut să fac cheia exportabilă și am reușit să fac acest lucru ca parte a Expertului de import al certificatelor MMC.

Sper că acest lucru ajută pe altcineva. Mulțumesc mult

utilizator1187372

Am întâlnit o problemă similară și X509KeyStorageFlags.Exportable mi-am rezolvat problema.

Jeroen Jacobs

Nu sunt chiar un expert în aceste lucruri, dar am făcut o căutare rapidă pe Google și am găsit asta:

http://social.msdn.microsoft.com/Forums/en/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce

„dacă aveți mai mult de 245 de octeți în matricea de octeți pe care o treceți la metoda RSACryptoServiceProvider.Encrypt(byte[] rgb, bool fOAEP), atunci se va arunca o excepție.”

Comentarii

  • Îmi cer scuze dacă nu m-am lămurit foarte bine, pot să sparg această linie: string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true)); into: var pvk = (x. PrivateKey as RSACryptoServiceProvider); var pvkParam = pvk.ExportParameters(true)); string foo = DecryptRSA(ed, pvkParam); …și dacă fac asta pvk.ExportParameters(true); va arunca. –  > Por kalrashi.
  • Această eroare este una dintre cele mai enervante erori scrise vreodată. Poate însemna atât de multe lucruri. Pentru mine, a fost așa cum a spus Jeroen: textul mesajului meu era prea mare pentru a fi criptat de cheie (este dezavantajul criptării asincrone). –  > Por Aejay.
Gijs

Pentru ceilalți care ajung aici prin Google, dar nu folosesc niciun X509Certificate2, dacă apelați ToXmlString pe RSACryptoServiceProvider, dar ați încărcat doar o cheie publică, veți primi și acest mesaj. Soluția este următoarea (rețineți ultima linie):

var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

var xml = rsaAlg.ToXmlString(!rsaAlg.PublicOnly);

poupou

AFAIK acest lucru ar trebui să funcționeze și probabil că vă loviți de un bug / unele limitări. Iată câteva întrebări care vă pot ajuta să vă dați seama unde este problema.

  • Cum ați creat fișierul PKCS#12 (PFX)? Am văzut unele chei care nu sunt pe placul CryptoAPI (parametri RSA neobișnuiți). Poți folosi un alt instrument (doar pentru a fi sigur) ?

  • Puteți exporta fișierul PrivateKey instanța în XML, de exemplu. ToXmlString(true), și apoi să o încărcați (importați) înapoi în acest mod?

  • Vechile versiuni ale cadrului au avut unele probleme atunci când importau o cheie care avea o dimensiune diferită de cea a instanței curente (implicit 1024 de biți). Care este dimensiunea cheii publice RSA din certificatul dumneavoastră?

Rețineți, de asemenea, că aceasta este nu modul în care ar trebui să criptați datele folosind RSA. Mărimea cifrării brute este limitată în raport cu cheia publică utilizată. Depășirea acestei limite vă va da doar într-adevăr performanțe foarte slabe.

Șmecheria constă în utilizarea unui algoritm simetric (cum ar fi AES) cu un total aleatorie și apoi criptează această cheie (wrap) cu ajutorul cheii publice RSA. Puteți găsi codul C# pentru a face acest lucru în vechiul meu fișier intrare pe blog pe această temă.

Flavio Bianchi

Postare veche, dar poate că poate ajuta pe cineva. dacă folosiți un certificat autofirmat și faceți autentificarea cu un alt utilizator, trebuie să ștergeți vechiul certificat din stocare și apoi să-l recreați. Am avut aceeași problemă cu software-ul opc ua.