dotnet-cryptography について
ハッシュ、暗号化、署名、鍵導出を扱う、.NET 開発者向けの暗号技術ガイドです。.NET 10 の耐量子アルゴリズムと、それ以前のターゲットフレームワークでの代替策も説明します。
主な機能
- 01SHA-2 ハッシュと HMAC
- 02AES-GCM による共通鍵暗号化
- 03RSA と ECDSA の署名
- 04PBKDF2 と Argon2 の鍵導出
- 05.NET 10 の耐量子暗号ガイド
- 06旧バージョン向けの代替策
活用シーン
- 01データ完全性を確認するハッシュの計算
- 02データの保護と真正性の検証
- 03パスワードハッシュと鍵導出の実装
- 04耐量子アルゴリズムへの移行計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-cryptographyインストール後、Claude Code で/dotnet-cryptographyを入力して読み込まれたか確認します。
詳細
dotnet-cryptography
ハッシュ(SHA-256/384/512)、対称暗号化(AES-GCM)、非対称暗号(RSA、ECDSA)、鍵導出(PBKDF2、Argon2)、耐量子アルゴリズム(ML-KEM、ML-DSA、SLH-DSA)を扱う、.NET 10+ 向けの最新の暗号技術です。TFM に応じたガイダンスも含まれています。net10.0 で利用できる機能と、net8.0/net9.0 での代替策を説明します。
対象外: シークレット管理と構成バインディングについては、[skill:dotnet-secrets-management] を参照してください。OWASP の脆弱性カテゴリと非推奨のセキュリティパターンについては、[skill:dotnet-security-owasp] を参照してください。認証/認可の実装(JWT、OAuth、Identity)については、[skill:dotnet-api-security] と [skill:dotnet-blazor-auth] を参照してください。クラウド固有の鍵管理(Azure Key Vault、AWS KMS)については、クラウドのエピックを参照してください。TLS/HTTPS の構成は ASP.NET Core ミドルウェアで扱います。
相互参照:[skill:dotnet-security-owasp] では OWASP A02(暗号化の失敗)と非推奨パターンに関する警告を、[skill:dotnet-secrets-management] では鍵とシークレットを安全に保存する方法を説明しています。
対象範囲
対象: アルゴリズムの選択、System.Security.Cryptography API の適切な使用、鍵導出、整合性確認のためのハッシュ、対称/非対称暗号化、耐量子暗号、非推奨アルゴリズムに関する警告。
対象外: 鍵の保存(シークレット管理を使用)、TLS 終端(インフラストラクチャ)、認証プロトコル([skill:dotnet-api-security] を参照)、クラウド HSM/KMS サービス(クラウドのエピック)。
前提条件
- .NET 8.0+(従来のアルゴリズムの LTS ベースライン)
- 耐量子アルゴリズム(ML-KEM、ML-DSA、SLH-DSA)には .NET 10.0+
- 耐量子暗号のプラットフォームサポート:Windows 11(2025 年 11 月以降)、または Linux/macOS 上の OpenSSL 3.5+
ハッシュ(SHA-2 ファミリー)
整合性の検証、チェックサム、コンテンツアドレス可能なストレージには SHA-256/384/512 を使用してください。パスワードにはハッシュのみを使用しないでください(下記の鍵導出を参照)。
using System.Security.Cryptography;
// Hash a byte array
byte[] data = "Hello, world"u8.ToArray();
byte[] hash = SHA256.HashData(data);
// Hash a stream (efficient for large files)
await using var stream = File.OpenRead("largefile.bin");
byte[] fileHash = await SHA256.HashDataAsync(stream);
// Compare hashes securely (constant-time comparison prevents timing attacks)
bool isEqual = CryptographicOperations.FixedTimeEquals(hash1, hash2);
// HMAC for authenticated hashing (message authentication codes)
byte[] key = RandomNumberGenerator.GetBytes(32); // 256-bit key
byte[] mac = HMACSHA256.HashData(key, data);
// Verify HMAC
byte[] computedMac = HMACSHA256.HashData(key, receivedData);
if (!CryptographicOperations.FixedTimeEquals(mac, computedMac))
{
throw new CryptographicException("Message authentication failed");
}
対称暗号化(AES-GCM)
AES-GCM は .NET で推奨される対称暗号化方式です。機密性と真正性の両方を提供します(関連データ付き認証暗号化 -- AEAD)。
using System.Security.Cryptography;
public static class AesGcmEncryptor
{
private const int NonceSize = 12; // 96-bit nonce (required by GCM)
private const int TagSize = 16; // 128-bit authentication tag
public static byte[] Encrypt(byte[] plaintext, byte[] key)
{
var nonce = RandomNumberGenerator.GetBytes(NonceSize);
var ciphertext = new byte[plaintext.Length];
var tag = new byte[TagSize];
using var aes = new AesGcm(key, TagSize);
aes.Encrypt(nonce, plaintext, ciphertext, tag);
// Prepend nonce + append tag for transport
var result = new byte[NonceSize + ciphertext.Length + TagSize];
nonce.CopyTo(result, 0);
ciphertext.CopyTo(result, NonceSize);
tag.CopyTo(result, NonceSize + ciphertext.Length);
return result;
}
public static byte[] Decrypt(byte[] encryptedData, byte[] key)
{
var nonce = encryptedData.AsSpan(0, NonceSize);
var ciphertext = encryptedData.AsSpan(NonceSize, encryptedData.Length - NonceSize - TagSize);
var tag = encryptedData.AsSpan(encryptedData.Length - TagSize);
var plaintext = new byte[ciphertext.Length];
using var aes = new AesGcm(key, TagSize);
aes.Decrypt(nonce, ciphertext, tag, plaintext);
return plaintext;
}
}
// ASP.NET Core Data Protection API -- preferred for web application scenarios
// Handles key management, rotation, and storage automatically
using Microsoft.AspNetCore.DataProtection;
public sealed class TokenProtector(IDataProtectionProvider provider)
{
private readonly IDataProtector _protector =
provider.CreateProtector("Tokens.V1");
public string Protect(string plaintext) => _protector.Protect(plaintext);
public string Unprotect(string ciphertext) => _protector.Unprotect(ciphertext);
}
// Registration:
builder.Services.AddDataProtection()
.SetApplicationName("MyApp")
.PersistKeysToFileSystem(new DirectoryInfo("/keys"));
非対称暗号(RSA、ECDSA)
RSA
小さなペイロードの暗号化(鍵ラップ)とデジタル署名には RSA を使用してください。鍵長は最低 2048 ビットとし、新しいシステムでは 4096 ビットを推奨します。
using System.Security.Cryptography;
// Generate an RSA key pair
using var rsa = RSA.Create(4096);
// Sign data
byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
// Verify signature (with public key)
byte[] publicKeyBytes = rsa.ExportRSAPublicKey();
using var rsaPublic = RSA.Create();
rsaPublic.ImportRSAPublicKey(publicKeyBytes, out _);
bool valid = rsaPublic.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
// Encrypt with OAEP padding (never use PKCS#1 v1.5 for new code)
byte[] encrypted = rsaPublic.Encrypt(smallPayload, RSAEncryptionPadding.OaepSHA256);
byte[] decrypted = rsa.Decrypt(encrypted, RSAEncryptionPadding.OaepSHA256);
ECDSA
新しいプロジェクトでデジタル署名を行う場合は、RSA より ECDSA を優先してください。同等のセキュリティをより短い鍵で実現できます。
using System.Security.Cryptography;
// Generate ECDSA key (P-256 = NIST curve, widely supported)
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
// Sign data
byte[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA256);
// Export public key for verification
byte[] publicKey = ecdsa.ExportSubjectPublicKeyInfo();
// Import and verify
using var ecdsaPublic = ECDsa.Create();
ecdsaPublic.ImportSubjectPublicKeyInfo(publicKey, out _);
bool valid = ecdsaPublic.VerifyData(data, signature, HashAlgorithmName.SHA256);
鍵導出(パスワードのハッシュ化)
PBKDF2(組み込み)
PBKDF2 は .NET に組み込まれており、パスワードのハッシュ化に適しています。SHA-256 を使い、反復回数は 600,000 回以上にしてください(OWASP の推奨)。
using System.Buffers.Binary;
using System.Security.Cryptography;
public static class PasswordHasher
{
private const int SaltSize = 16; // 128-bit salt
private const int HashSize = 32; // 256-bit derived key
private const int Iterations = 600_000; // OWASP 2023 recommendation for SHA-256
private const int PayloadSize = 4 + SaltSize + HashSize; // iteration count + salt + hash
public static string HashPassword(string password)
{
byte[] salt = RandomNumberGenerator.GetBytes(SaltSize);
byte[] hash = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
Iterations,
HashAlgorithmName.SHA256,
HashSize);
// Store iteration count (fixed little-endian), salt, and hash together
byte[] result = new byte[PayloadSize];
BinaryPrimitives.WriteInt32LittleEndian(result, Iterations);
salt.CopyTo(result.AsSpan(4));
hash.CopyTo(result.AsSpan(4 + SaltSize));
return Convert.ToBase64String(result);
}
public static bool VerifyPassword(string password, string stored)
{
// Defensive parsing: reject malformed input without exceptions
Span<byte> decoded = stackalloc byte[PayloadSize];
if (!Convert.TryFromBase64String(stored, decoded, out int bytesWritten)
|| bytesWritten != PayloadSize)
{
return false;
}
int iterations = BinaryPrimitives.ReadInt32LittleEndian(decoded);
if (iterations <= 0)
return false;
var salt = decoded.Slice(4, SaltSize);
var expectedHash = decoded.Slice(4 + SaltSize, HashSize);
byte[] actualHash = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
iterations,
HashAlgorithmName.SHA256,
HashSize);
return CryptographicOperations.FixedTimeEquals(expectedHash, actualHash);
}
}
Argon2(NuGet 経由)
NuGet の依存関係を許容できる場合、パスワードのハッシュ化には Argon2id を推奨します。メモリハードなアルゴリズムであり、PBKDF2 よりも GPU/ASIC 攻撃への耐性に優れています。
// Requires: <PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.*" />
using Konscious.Security.Cryptography;
public static byte[] HashWithArgon2(string password, byte[] salt)
{
using var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 4, // threads
MemorySize = 65536, // 64 MB
Iterations = 3
};
return argon2.GetBytes(32); // 256-bit hash
}
Web アプリケーションでは、ASP.NET Core Identity の
PasswordHasher<T>を優先してください。適切なパラメーターと形式のバージョン管理を使って PBKDF2 を自動的に処理します。カスタムのハッシュ化は、Identity を使用しない場合に限ってください。
耐量子暗号(.NET 10+)
.NET 10 では、System.Security.Cryptography 名前空間を通じて耐量子暗号(PQC)が導入されます。これらのアルゴリズムは、従来型コンピューターと量子コンピューターの両方からの攻撃に耐性があります。
プラットフォーム要件
PQC API を利用するには、OS レベルのサポートが必要です。
- Windows: Windows 11(2025 年 11 月の更新プログラム)または PQC 更新プログラムを適用した Windows Server 2025
- Linux/macOS: OpenSSL 3.5 以降
PQC 型を使用する前に、必ず IsSupported を確認してください。サポートされていないプラットフォームでは、従来のアルゴリズムにフォールバックしてください。
ML-KEM(FIPS 203)――鍵カプセル化
ML-KEM は、共有秘密を確立するために、従来の鍵交換(ECDH)に代わるものです。最も成熟した .NET 10 の PQC API であり、クラスレベルでは [Experimental] としてマークされていません。
#if NET10_0_OR_GREATER
using System.Security.Cryptography;
if (!MLKem.IsSupported)
{
Console.WriteLine("ML-KEM not available on this platform");
return;
}
// Generate a key pair
using MLKem privateKey = MLKem.GenerateKey(MLKemAlgorithm.MLKem768);
// Export public encapsulation key (share with peer)
byte[] publicKeyBytes = privateKey.ExportEncapsulationKey();
// Peer: import public key and encapsulate a shared secret
using MLKem publicKey = MLKem.ImportEncapsulationKey(
MLKemAlgorithm.MLKem768, publicKeyBytes);
publicKey.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret1);
// Original holder: decapsulate to recover the same shared secret
byte[] sharedSecret2 = privateKey.Decapsulate(ciphertext);
// Both parties now have the same shared secret for symmetric encryption
bool match = sharedSecret1.AsSpan().SequenceEqual(sharedSecret2);
#endif
パラメーターセット:
| パラメーターセット | セキュリティレベル | カプセル化鍵 | 暗号文 |
|---|---|---|---|
MLKemAlgorithm.MLKem512 | NIST レベル 1(128 ビット) | 800 バイト | 768 バイト |
MLKemAlgorithm.MLKem768 | NIST レベル 3(192 ビット) | 1,184 バイト | 1,088 バイト |
MLKemAlgorithm.MLKem1024 | NIST レベル 5(256 ビット) | 1,568 バイト | 1,568 バイト |
一般的な用途には MLKem768 を推奨します(セキュリティとパフォーマンスのバランスが取れています)。
ML-DSA(FIPS 204)――デジタル署名
ML-DSA は、量子耐性のあるデジタル署名のために、RSA/ECDSA に代わるものです。
#if NET10_0_OR_GREATER
using System.Security.Cryptography;
if (!MLDsa.IsSupported)
{
Console.WriteLine("ML-DSA not available on this platform");
return;
}
// Generate signing key
using MLDsa key = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65);
// Sign data
byte[] data = "Document to sign"u8.ToArray();
byte[] signature = new byte[key.Algorithm.SignatureSizeInBytes];
key.SignData(data, signature);
// Export public key for verification
byte[] publicKeyBytes = key.ExportMLDsaPublicKey();
// Verify with public key
using MLDsa publicKey = MLDsa.ImportMLDsaPublicKey(
MLDsaAlgorithm.MLDsa65, publicKeyBytes);
bool valid = publicKey.VerifyData(data, signature);
#endif
パラメーターセット:
| パラメーターセット | セキュリティレベル | 公開鍵 | 署名 |
|---|---|---|---|
MLDsaAlgorithm.MLDsa44 | NIST レベル 2 | 1,312 バイト | 2,420 バイト |
MLDsaAlgorithm.MLDsa65 | NIST レベル 3 | 1,952 バイト | 3,309 バイト |
MLDsaAlgorithm.MLDsa87 | NIST レベル 5 | 2,592 バイト | 4,627 バイト |
SLH-DSA(FIPS 205)――ハッシュベース署名
SLH-DSA(ステートレス・ハッシュベース・デジタル署名アルゴリズム)は、非常に保守的な長期署名を提供します。格子ベース方式(ML-DSA)の数学的構造が懸念される場合に使用してください。SlhDsa クラス全体が [Experimental](SYSLIB5006)です。Windows では、まだネイティブサポートが追加されていません。
#if NET10_0_OR_GREATER
using System.Security.Cryptography;
// SlhDsa is [Experimental] -- suppress SYSLIB5006 only when intentional
#pragma warning disable SYSLIB5006
if (SlhDsa.IsSupported)
{
using SlhDsa key = SlhDsa.GenerateKey(SlhDsaAlgorithm.SlhDsaSha2_128s);
byte[] data = "Long-term document"u8.ToArray();
byte[] signature = new byte[key.Algorithm.SignatureSizeInBytes];
key.SignData(data, signature);
bool valid = key.VerifyData(data, signature);
}
#pragma warning restore SYSLIB5006
#endif
net8.0/net9.0 向けのフォールバック戦略
ポスト量子アルゴリズムは .NET 10 以降でのみ利用できます。それ以前の TFM を対象とするアプリケーションでは、次のようにしてください。
- 今は従来のアルゴリズムを使用する: 署名には ECDSA(P-256/P-384)、鍵交換/暗号化には ECDH + AES-GCM を使用してください。これらは従来型の攻撃に対して引き続き安全です。
- 移行に備える: 暗号化処理をインターフェイスの背後に分離し、アルゴリズムの切り替えに必要なコード変更を最小限に抑えてください。
- 準備ができたらマルチターゲットにする:
#if NET10_0_OR_GREATER条件または TFM ごとの個別アセンブリを使用して、従来のフォールバックと並行して PQC サポートを追加してください。 - 今収集し、後で復号する攻撃への備え: 10 年以上にわたって機密性を保つ必要があるデータについては、取得された暗号文が将来量子コンピューターで復号されるリスクから保護するため、より早く .NET 10 に移行することを検討してください。
相互運用性に関する注意事項
- 鍵と署名のサイズ: PQC の鍵と署名は、従来の方式のものより大幅に大きくなります(例:ML-DSA-65 の署名は 3,309 バイト、ECDSA P-256 は 64 バイト)。ストレージ、帯域幅、プロトコルメッセージのサイズに影響します。
- PQC のクロスプラットフォーム対応はまだありません: PQC API は OS の暗号ライブラリに依存します。net10.0 向けにコンパイルされたアプリは、古い OS バージョンでは実行時に失敗します。必ず
IsSupportedで使用可否を確認してください。 - PKCS#8/X.509 形式は試験的です: PQC 鍵の標準証明書形式でのインポート/エクスポートは、IETF RFC の最終化を待つ間、
[Experimental]です。現時点では、本番環境で PQC 鍵を PKCS#8 形式で永続化しないでください。 - 複合/ハイブリッド署名:
CompositeMLDsa(ハイブリッド ML-DSA + 従来方式)は完全に[Experimental]で、OS のネイティブサポートもありません。プロトタイピングにのみ使用してください。 - TLS との統合: ML-DSA および SLH-DSA 証明書は
SslStreamを介して TLS 1.3 以降で動作しますが、OS の暗号ライブラリが TLS での PQC をサポートしている場合に限ります。デプロイ先で確認してください。 - パフォーマンス: ML-KEM と ML-DSA は高速です。SLH-DSA の署名は大幅に遅く(ミリ秒ではなく秒単位)、ハッシュベースのセキュリティ保証が必要な場合にのみ使用してください。
非推奨の暗号 API
以下の暗号アルゴリズムは脆弱または時代遅れです。新しいコードでは使用しないでください。
| アルゴリズム | 代替手段 | 理由 |
|---|---|---|
| MD5 | SHA-256+ | 2004 年以降、衝突攻撃が可能であり、容易に破られます |
| SHA-1 | SHA-256+ | 衝突攻撃が実証されています(SHAttered、2017 年) |
| DES | AES-GCM | 56 ビット鍵で、数時間で総当たり攻撃が可能です |
| 3DES (TripleDES) | AES-GCM | NIST により非推奨となりました(2023 年)。Sweet32 攻撃の対象です |
| RC2 | AES-GCM | 鍵スケジュールが弱く、実効鍵長が公称値を下回ります |
| RSA PKCS#1 v1.5 encryption | RSA-OAEP | Bleichenbacher パディングオラクル攻撃 |
暗号以外の非推奨なセキュリティパターン(CAS、APTCA、.NET Remoting、DCOM、BinaryFormatter)の全一覧については、非推奨のセキュリティパターンに関する警告を一元管理する [skill:dotnet-security-owasp] を参照してください。
エージェントが注意すべき点
- AES-GCM でノンスを再利用しないでください -- 同じ鍵でノンスを再利用すると、機密性と真正性の両方が損なわれます。暗号化処理ごとに、必ず新しいランダムなノンスを生成してください。
- ECB モードは使用しないでください -- ECB では同一の平文ブロックが同一の暗号文ブロックに暗号化されるため、パターンが漏えいします。.NET の
Aes.Create()の既定値は CBC ですが、認証付き暗号化には AES-GCM を推奨します。 - ハッシュを
==で比較しないでください -- タイミングサイドチャネル攻撃を防ぐため、CryptographicOperations.FixedTimeEqualsを使用してください。 - セキュリティ目的で MD5 または SHA-1 を使用しないでください -- これらは脆弱です。SHA-1 が許容されるのは、衝突耐性がセキュリティ要件ではない非セキュリティ用途のチェックサム(例:git オブジェクトハッシュ)に限られます。
- 暗号化鍵をハードコードしないでください -- 鍵の保管には [skill:dotnet-secrets-management] を使用してください。
RandomNumberGenerator.GetBytesで鍵を生成してください。 - RSA 鍵の最小サイズは 2048 ビットです -- NIST は 1024 ビットの RSA 鍵を非推奨としています。新しいシステムでは 4096 ビットを使用してください。
- PBKDF2 の反復回数は十分に大きくする必要があります -- OWASP は SHA-256 を使用した 600,000 回の反復を推奨しています(2023 年時点)。これより少ない回数では総当たり攻撃が可能です。
- PQC の
IsSupportedチェックは必須です -- サポートされていないプラットフォームで PQC API を呼び出すと、PlatformNotSupportedExceptionがスローされます。使用前に必ずチェックしてください。 - SYSLIB5006 をグローバルに抑制しないでください -- 実験的な PQC API を意図的に使用する特定の呼び出し箇所でのみ、実験的であることを示す診断を抑制してください。