TL; DR; Nie znalazłem sposobu na wyprowadzenie klucza symetrycznego za pomocą KDF opisanego w NIST SP 800-56A, rozdział 5.8.1 przy użyciu wbudowanych klas samego .NET 4.0
Dobra wiadomość (dla mnie :-)) jest to możliwe w .NET 4.0 za pomocą pięknej biblioteki BouncyCastle (NuGet: Install-Package BouncyCastle-Ext -Version "1.7.0"). Oto jak:
KROK 1: Get druga strona public key
W zależności od sytuacji, może to być odczytane z certyfikatem lub przyjść do was jako części wiadomości zawierającej zaszyfrowane dane. Raz masz Base64 zakodowanego klucza publicznego, przeczytaj ją w Org.BouncyCastle.Crypto.Parameters.ECPublicKeyParameters obiekt tak:
var publicKeyBytes = Convert.FromBase64String(base64PubKeyStr);
ECPublicKeyParameters otherPartyPublicKey = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
KROK 2: Przeczytaj swój prywatny klucz
To MOST zwykle wymagają przeczytania klucza prywatnego z certyfikatu PFX/P12. Konto systemu Windows z uruchomionym kodem powinno mieć dostęp do PFX/P12 i dodatkowo, jeśli certyfikat jest importowany do magazynu certyfikatów, musisz udzielić uprawnień za pośrednictwem menu Wszystkie zadania -> zarządzaj kluczem prywatnym w certmgr.msc
using (StreamReader reader = new StreamReader(path))
{
var fs = reader.BaseStream;
string password = "<password for the PFX>";
Pkcs12Store store = new Pkcs12Store(fs, passWord.ToCharArray());
foreach (string n in store.Aliases)
{
if (store.IsKeyEntry(n))
{
AsymmetricKeyEntry asymmetricKey = store.GetKey(n);
if (asymmetricKey.Key.IsPrivate)
{
ECPrivateKeyParameters privateKey = asymmetricKey.Key as ECPrivateKeyParameters;
}
}
}
}
KROK 3: oblicz wspólne hasło
IBasicAgreement aKeyAgree = AgreementUtilities.GetBasicAgreement("ECDH");
aKeyAgree.Init(privateKey);
BigInteger sharedSecret = aKeyAgree.CalculateAgreement(otherPartyPublicKey);
byte[] sharedSecretBytes = sharedSecret.ToByteArray();
KROK 4: Przygotuj informacje wymagane do obliczenia klucza symetrycznego:
byte[] algorithmId = Encoding.ASCII.GetBytes(("<prependString/Hex>" + "id-aes256-GCM"));
byte[] partyUInfo = Encoding.ASCII.GetBytes("<as-per-agreement>");
byte[] partyVInfo = <as-per-agreement>;
MemoryStream stream = new MemoryStream(algorithmId.Length + partyUInfo.Length + partyVInfo.Length);
var sr = new BinaryWriter(stream);
sr.Write(algorithmId);
sr.Flush();
sr.Write(partyUInfo);
sr.Flush();
sr.Write(partyVInfo);
sr.Flush();
stream.Position = 0;
byte[] keyCalculationInfo = stream.GetBuffer();
Krok 5: Wyprowadź SY klucz mmetric
// NOTE: Use the digest/Hash function as per your agreement with the other party
IDigest digest = new Sha256Digest();
byte[] symmetricKey = new byte[digest.GetDigestSize()];
digest.Update((byte)(1 >> 24));
digest.Update((byte)(1 >> 16));
digest.Update((byte)(1 >> 8));
digest.Update((byte)1);
digest.BlockUpdate(sharedSecret, 0, sharedSecret.Length);
digest.BlockUpdate(keyCalculationInfo, 0, keyCalculationInfo.Length);
digest.DoFinal(symmetricKey, 0);
Teraz masz klucz symetryczny gotowy do deszyfrowania. Aby wykonać deszyfrowanie za pomocą AES, można użyć BouncyCastle IWrapper. Uzyskaj IWrapper za pomocą Org.BouncyCastle.Security.WrapperUtilities, wywołując WrapperUtilities.GetWrapper ("AES //") np. "AES/CBC/PKCS7". Będzie to również zależało od porozumienia między dwiema stronami, które się komunikują.
Zainicjuj szyfr (IWrapper) za pomocą klucza symetrycznego i wektora inicjalizacyjnego (IV) i wywołaj metodę Unwrap, aby pobrać bajty zwykłego tekstu. Wreszcie przekonwertować ciąg dosłownym stosując kodowanie znaków używane (np UTF8/ASCII/Unicode)
Na stronie szyfrowania BouncyCastle ma klasę ConcatenationKDFGenerator https://github.com/bcgit/bc-csharp/blob/0801c1543f0cafc79c44b225e53c973bdd1b0a0f/ crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs – Sentinel