Blazor wasm で System.Security.Cryptography API の代わりに使えそうな暗号ライブラリ
.NET 5 から Blazor WebAssembly では System.Security.Cryptography API がサポートされません。代わりに使えそうな暗号ライブラリを試してみました。
Bouncy Castle
Bouncy Castle は、Java と C# で提供されている暗号ライブラリ。GitHub リポジトリ bcgit/bc-csharp を fork して、個人が NuGet BouncyCastle.NetCore を公開されています。
※ Blazor で下記の AES・HMAC のコードを動作確認済み
CryptographyHelpers (CryptHash.NET)
CryptHash.NET は、Windows/Linux/Mac で動作するよう設計された .NET Standard 2.0/2.1 の暗号ライブラリ。現在、.NET 5 ライブラリ CryptographyHelpers として移行しています。NuGet でも公開されています。
※ Blazor で動作未確認です
比較: AES
必要だったのが AES と HMAC-SHA 関連のみだったので、それ以外は調べていません。
System.Security.Cryptography
まずは、Blazor WebAssembly で使うには、移行が必要なコードサンプル。Aes コンストラクター (System.Security.Cryptography) | Microsoft Docs のサンプルコードを元にしたコード。
// Encrypt | |
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] key, byte[] iv) | |
{ | |
// Create an Aes object | |
// with the specified key and IV. | |
using var aesAlg = Aes.Create(); | |
aesAlg.Key = key; | |
aesAlg.IV = iv; | |
// Create an encryptor to perform the stream transform. | |
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); | |
// Create the streams used for encryption. | |
using var msEncrypt = new MemoryStream(); | |
using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write); | |
using (var swEncrypt = new StreamWriter(csEncrypt)) | |
{ | |
//Write all data to the stream. | |
swEncrypt.Write(plainText); | |
} | |
var encrypted = msEncrypt.ToArray(); | |
// Return the encrypted bytes from the memory stream. | |
return encrypted; | |
} | |
// Decrypt | |
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] key, byte[] iv) | |
{ | |
// Declare the string used to hold | |
// the decrypted text. | |
string plaintext = null; | |
// Create an Aes object | |
// with the specified key and IV. | |
using var aesAlg = Aes.Create(); | |
aesAlg.Key = key; | |
aesAlg.IV = iv; | |
// Create a decryptor to perform the stream transform. | |
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); | |
// Create the streams used for decryption. | |
using var msDecrypt = new MemoryStream(cipherText); | |
using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read); | |
using var srDecrypt = new StreamReader(csDecrypt); | |
// Read the decrypted bytes from the decrypting stream | |
// and place them in a string. | |
plaintext = srDecrypt.ReadToEnd(); | |
return plaintext; | |
} |
Bouncy Castle
Bouncy Castle を用いたコード。c# - Encrypt string with Bouncy Castle AES/CBC/PKCS7 - Stack Overflow を参考にしています。
// Encrypt | |
static byte[] EncryptStringToBytes_Aes_BouncyCastle(string plainText, byte[] key, byte[] iv) | |
{ | |
var input = Encoding.UTF8.GetBytes(plainText); | |
var engine = new AesEngine(); | |
var blockCipher = new CbcBlockCipher(engine); | |
var cipher = new PaddedBufferedBlockCipher(blockCipher); // PKCS5/7 padding | |
var keyParam = new KeyParameter(key); | |
var keyParamWithIV = new ParametersWithIV(keyParam, iv); | |
// Encrypt | |
cipher.Init(true, keyParamWithIV); | |
var encrypted = new byte[cipher.GetOutputSize(input.Length)]; | |
var length = cipher.ProcessBytes(input, encrypted, 0); | |
cipher.DoFinal(encrypted, length); | |
return encrypted; | |
} | |
// Decrypt | |
static string DecryptStringFromBytes_Aes_BouncyCastle(byte[] cipherText, byte[] key, byte[] iv) | |
{ | |
var engine = new AesEngine(); | |
var blockCipher = new CbcBlockCipher(engine); | |
var cipher = new PaddedBufferedBlockCipher(blockCipher); // PKCS5/7 padding | |
var keyParam = new KeyParameter(key); | |
var keyParamWithIV = new ParametersWithIV(keyParam, iv); | |
// Decrypt | |
cipher.Init(false, keyParamWithIV); | |
var decrypted = new byte[cipher.GetOutputSize(cipherText.Length)]; | |
var length = cipher.ProcessBytes(cipherText, decrypted, 0); | |
cipher.DoFinal(decrypted, length); | |
return Encoding.UTF8.GetString(decrypted).TrimEnd('\0'); | |
} |
CryptHash.NET
CryptHash.NET は、AES の基本の暗号化・複合処理は、抽象クラスの internal メソッドのため全く同じことはできません。パスワードの暗号化や簡潔に書ける便利そうなオーバーロードが、いろいろあるのでオリジナルのコードを読んでみてください。
static byte[] EncryptStringToBytes_Aes_CryptHashNet(string plainText, byte[] key, byte[] iv, out byte[] authenticationKey, out byte[] tag) | |
{ | |
var aes = new AE_AES_128_CBC_HMAC_SHA_256(); | |
var result = aes.EncryptString(Encoding.UTF8.GetBytes(plainText), key, iv); | |
authenticationKey = result.AuthenticationKey; | |
tag = result.Tag; | |
return result.EncryptedDataBytes; | |
} | |
static string DecryptStringFromBytes_Aes_CryptHashNet(byte[] cipherText, byte[] key, byte[] iv, byte[] authenticationKey, byte[] sentTag) | |
{ | |
var aes = new AE_AES_128_CBC_HMAC_SHA_256(); | |
var result = aes.DecryptString(cipherText, key, iv, authenticationKey, sentTag); | |
return result.DecryptedDataString; | |
} |
呼び出しサンプル
上記メソッドを呼び出す部分のコードです。
static void Main(string[] args) | |
{ | |
var text = "山路を登りながら、こう考えた。"; | |
var key = Encoding.UTF8.GetBytes("xxxxxxxxxxxxxxxx"); | |
var iv = Encoding.UTF8.GetBytes("0123456789abcdef"); | |
// System.Security.Cryptography | |
{ | |
var cipherText = EncryptStringToBytes_Aes(text, key, iv); | |
Console.WriteLine($"cipherText: {Convert.ToBase64String(cipherText)}"); | |
var plainText = DecryptStringFromBytes_Aes(cipherText, key, iv); | |
Console.WriteLine($"plainText: {plainText}"); | |
} | |
// Bouncy Castle | |
{ | |
var cipherText = EncryptStringToBytes_Aes_BouncyCastle(text, key, iv); | |
Console.WriteLine($"cipherText: {Convert.ToBase64String(cipherText)}"); | |
var plainText = DecryptStringFromBytes_Aes_BouncyCastle(cipherText, key, iv); | |
Console.WriteLine($"plainText: {plainText}"); | |
} | |
// CryptHash.Net | |
{ | |
var cipherText = EncryptStringToBytes_Aes_CryptHashNet(text, key, iv, out var authenticationKey, out var tag); | |
Console.WriteLine($"cipherText: {Convert.ToBase64String(cipherText)}"); | |
var plainText = DecryptStringFromBytes_Aes_CryptHashNet(cipherText, key, iv, authenticationKey, tag); | |
Console.WriteLine($"plainText: {plainText}"); | |
} | |
} |
比較: HMAC
HMAC-SHA-1 の処理比較です。
static void Main(string[] args) | |
{ | |
var text = "山路を登りながら、こう考えた。"; | |
var key = Encoding.UTF8.GetBytes("xxxxxxxxxxxxxxxx"); | |
{ | |
var hash = HmacSha1(key, Encoding.UTF8.GetBytes(text)); | |
Console.WriteLine($"hash: {Convert.ToBase64String(hash)}"); | |
} | |
{ | |
var hash = HmacSha1_BouncyCastle(key, Encoding.UTF8.GetBytes(text)); | |
Console.WriteLine($"hash(BouncyCastle): {Convert.ToBase64String(hash)}"); | |
} | |
{ | |
var hash = HmacSha1_CryptHashNet(key, Encoding.UTF8.GetBytes(text)); | |
Console.WriteLine($"hash(CryptHashNet): {Convert.ToBase64String(hash)}"); | |
} | |
} | |
// System.Security.Cryptography | |
static byte[] HmacSha1(byte[] key, byte[] message) | |
{ | |
using var hmac = new HMACSHA1(key); | |
return hmac.ComputeHash(message); | |
} | |
// Bouncy Castle | |
static byte[] HmacSha1_BouncyCastle(byte[] key, byte[] message) | |
{ | |
var hmac = new HMac(new Sha1Digest()); | |
var output = new byte[hmac.GetMacSize()]; | |
hmac.Init(new KeyParameter(key)); | |
hmac.BlockUpdate(message, 0, message.Length); | |
hmac.DoFinal(output, 0); | |
return output; | |
} | |
// CryptHash.Net | |
static byte[] HmacSha1_CryptHashNet(byte[] key, byte[] message) | |
{ | |
var hmac = new HMAC_SHA_1(); | |
var result = hmac.ComputeHMAC(message, key); | |
return result.HashBytes; | |
} |