VNX-CS-009 – C# Use of Weak Cryptographic Algorithm (MD5, SHA-1, DES, 3DES, RC2, RC4)
Overview
This rule reports a line of C# (.cs) that creates or calls a broken or deprecated cryptographic primitive. It matches the API call, not the algorithm’s name, so an enum member DigestKind.MD5, a property called SHA1, OrderByDescending or a log message naming MD5.Create() is not a finding. What it reports:
- Hashes:
MD5.Create(),SHA1.Create(), the one-shotMD5.HashData(),SHA1.HashData(),HashDataAsync()andTryHashData()(also fully qualified, split across two lines, or through ausing Alias = System.Security.Cryptography.MD5;alias), and the classesMD5CryptoServiceProvider,MD5Cng,SHA1Managed,SHA1CryptoServiceProviderandSHA1Cng(new X(),new X { ... },X x = new();). - Ciphers:
DES.Create(),TripleDES.Create(),RC2.Create()andDESCryptoServiceProvider,TripleDESCryptoServiceProvider,TripleDESCng,RC2CryptoServiceProvider. - By name:
HashAlgorithm.Create("MD5"),SymmetricAlgorithm.Create("TripleDES"),CryptoConfig.CreateFromName("SHA1"),CryptoConfig.MapNameToOID("SHA1")andformatter.SetHashAlgorithm("SHA1"). - Hashing and signing with a weak
HashAlgorithmName:IncrementalHash.CreateHash(HashAlgorithmName.MD5),CryptographicOperations.HashData(HashAlgorithmName.MD5, ...),SignData/SignHash/VerifyData/VerifyHashandnew CertificateRequest(...)withHashAlgorithmName.SHA1orMD5, also when the argument sits on its own line of a multi-line call or is a variable or field the file assigns fromHashAlgorithmName.SHA1/MD5(var alg = HashAlgorithmName.SHA1;thenrsa.VerifyData(data, sig, alg, ...)). The .NET Framework overloads that name the hash as a string:rsa.SignData(data, "SHA1"),rsa.VerifyData(data, "MD5", sig). An XML signature whoseSignatureMethodorDigestMethodis set toSignedXml.XmlDsigRSASHA1Url,XmlDsigDSAUrlorXmlDsigSHA1Url. - PBKDF1:
new PasswordDeriveBytes(...). - BouncyCastle:
MD5Digest,MD4Digest,MD2Digest,Sha1Digest,DesEngine,DesEdeEngine,RC2Engine,RC4Engine, andDigestUtilities.GetDigest("MD5"),CipherUtilities.GetCipher("DES/CBC/..."),SignerUtilities.GetSigner("SHA1withRSA").
Deliberately not reported:
- HMAC constructions (
HMACSHA1,HMACMD5,IncrementalHash.CreateHMAC). HMAC does not depend on collision resistance, and TOTP and several webhook schemes fix HMAC-SHA1. - RSA-OAEP with SHA-1 (
RSAEncryptionPadding.OaepSHA1) and PBKDF2 (Rfc2898DeriveBytes), whatever its hash. - A
HashAlgorithmName.SHA1that is compared, mapped in aswitchor rejected rather than used. - A hash whose surrounding code shows a protocol-fixed, non-security use: the WebSocket
Sec-WebSocket-Acceptkey (RFC 6455), Gravatar URLs, ETags, Content-MD5 /ContentHash, and name-based GUIDs. - Code under an explicit analyzer suppression:
#pragma warning disable CA5350,CA5351orCA5373(until the matchingrestore), or a[SuppressMessage(..., "CA5351...")]on the member. - Comments, string literals, and test projects (
tests/,*.Tests/,*.UnitTests/,*Tests.cs). - ECB mode and an algorithm chosen at run time from a variable (
HashAlgorithm.Create(name)) are outside this rule.
MD5 and SHA-1 are hash functions with known collision attacks. MD5 collisions can be computed in seconds on modern hardware, and chosen-prefix collisions are practical; SHA-1 suffered its first practical chosen-prefix collision in 2020. Using either for integrity or signatures lets an attacker substitute data without changing the digest.
DES has a 56-bit key, which is exhaustively searchable in hours. RC2 and RC4 are similarly weak. TripleDES offers at most 112 bits of security, has a 64-bit block (Sweet32), is deprecated by NIST, and is slower than AES.
Severity: High | CWE: CWE-327 – Use of a Broken or Risky Cryptographic Algorithm
Why This Matters
The practical break of MD5 has had real security consequences. The Flame malware (2012) used an MD5 collision attack to forge a valid Microsoft code-signing certificate, allowing it to appear as legitimately signed Windows Update software. This allowed the malware to spread within networks that trusted Microsoft’s signing infrastructure. SHA-1 TLS certificates were deprecated by all major browsers by 2017 precisely because chosen-prefix collisions make certificate forgery practical.
For password storage, MD5 and SHA-1 are catastrophically weak: they are fast hash functions designed for speed, and modern GPUs compute billions of MD5 or SHA-1 hashes per second, reducing the time to crack a password database from years to hours even with salted hashes. Password hashing must use purpose-built slow KDFs: PBKDF2, bcrypt, scrypt, or Argon2.
In .NET these algorithms remain available for legacy interoperability, and the .NET analyzers CA5350 and CA5351 flag them. The *CryptoServiceProvider and *Managed classes are obsolete (SYSLIB0021) in current .NET. CAPEC-97 (Cryptanalysis) and ATT&CK T1600 both relate to this class of cryptographic weakness.
What Gets Flagged
// FLAGGED: MD5 for a password hash
using var md5 = MD5.Create();
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(salt + password));
// FLAGGED: one-shot SHA-1
return SHA1.HashData(Encoding.UTF8.GetBytes(token));
// FLAGGED: DES and 3DES
using var des = new DESCryptoServiceProvider();
var tdes = new TripleDESCryptoServiceProvider { Key = key, Mode = CipherMode.CBC };
// FLAGGED: algorithm requested by name
return (HashAlgorithm)CryptoConfig.CreateFromName("SHA1");
// FLAGGED: SHA-1 signature
return _rsa.SignData(payload, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
// NOT FLAGGED: HMAC-SHA1 for TOTP, SHA-1 fixed by the WebSocket protocol
using var hmac = new HMACSHA1(secret);
var accept = SHA1.HashData(Encoding.ASCII.GetBytes(clientKey + WebSocketGuid));
Remediation
- For hashing data integrity (files, documents, non-password data): use SHA-256 (
SHA256.Create()) or SHA-3 (SHA3_256.Create()in .NET 8+). - For password hashing: use
Rfc2898DeriveBytes(PBKDF2 with SHA-256) with at least 310,000 iterations, or use a third-party library providing bcrypt or Argon2. - For symmetric encryption: replace DES/3DES/RC2/RC4 with AES using GCM mode (
AesGcmclass, .NET Core 3.0+) for authenticated encryption. - For signatures and certificates: pass
HashAlgorithmName.SHA256or stronger toSignData,VerifyDataandCertificateRequest, and useSignedXml.XmlDsigRSASHA256Url/XmlDsigSHA256Urlfor XML signatures. - For new MAC designs prefer
HMACSHA256orHMACSHA512. HMAC-SHA1 is not reported, since it remains acceptable where a protocol such as TOTP fixes it.
// SAFE: SHA-256 for data integrity
using var sha256 = SHA256.Create();
byte[] hash = sha256.ComputeHash(fileBytes);
// SAFE: PBKDF2 with SHA-256 for password hashing
using var pbkdf2 = new Rfc2898DeriveBytes(
password,
salt: RandomNumberGenerator.GetBytes(32),
iterations: 310_000,
hashAlgorithm: HashAlgorithmName.SHA256);
byte[] passwordHash = pbkdf2.GetBytes(32);
// SAFE: AES-GCM for authenticated encryption
byte[] key = RandomNumberGenerator.GetBytes(32);
byte[] nonce = RandomNumberGenerator.GetBytes(AesGcm.NonceByteSizes.MaxSize);
byte[] ciphertext = new byte[plaintext.Length];
byte[] tag = new byte[AesGcm.TagByteSizes.MaxSize];
using var aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MaxSize);
aesGcm.Encrypt(nonce, plaintext, ciphertext, tag);