Skip to content

Instantly share code, notes, and snippets.

@richlander
Last active June 4, 2022 18:45
Show Gist options
  • Save richlander/52423b87b60d39d8b464 to your computer and use it in GitHub Desktop.
Save richlander/52423b87b60d39d8b464 to your computer and use it in GitHub Desktop.
.NET Cryptography Updates for the .NET Framework 4.6
// Example 1: Signing a byte[] using PKCS#1 v1.5 padding and a SHA-256 hash
// 4.5:
public static byte[] SignDataPkcs1Sha256(X509Certificate2 cert, byte[] data)
{
// X509Certificate2.PrivateKey returns the same object across multiple calls,
// so it shouldn't be Disposed independent of the X509Certificate2 object.
//
// The RSA base class doesn't expose any signature-based methods.
// The PrivateKey property returns AsymmetricAlgorithm, so really this call should be
// done via 'as', or another safe flow... but it's almost always seen as an explicit cast
RSACryptoServiceProvider rsaCsp = (RSACryptoServiceProvider)cert.PrivateKey;
// SignData's second parameter is of type object. This leads to bad discoverability,
// and debate about what the 'right' answer should be.
return rsaCsp.SignData(data, "SHA256");
}
// 4.6:
public static byte[] SignDataPkcs1Sha256(X509Certificate2 cert, byte[] data)
{
// GetRSAPrivateKey returns an object with an independent lifetime, so it should be
// handled via a using statement.
using (RSA rsa = cert.GetRSAPrivateKey())
{
// RSA now exposes SignData, and the hash algorithm parameter takes a strong type,
// which allows for IntelliSense hints.
return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
// Example 2: Signing a byte[] using PSS padding and a SHA-256 hash.
// 4.5: Not possible
// 4.6:
public static byte[] SignDataPssSha256(X509Certificate2 cert, byte[] data)
{
using (RSA rsa = cert.GetRSAPrivateKey())
{
// RSA's SignData method exposes the signature padding type.
// Pkcs1 was the only padding that .NET 4.5 could do.
//
// This value must match on both Sign and Verify, so application developers
// are cautioned to consider their support matrix before upgrading from
// Pkcs1 (PKCS#1 v1.5) to Pss (PKCS#1 v2.1, Probabilistic Signature Scheme)
return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
}
}
// Example 3: Encrypting a byte[] using OAEP(-SHA1)
// 4.5:
public static byte[] EncryptDataOaepSha1(X509Certificate2 cert, byte[] data)
{
// X509Certificate2.PublicKey.Key returns the same object across multiple calls,
// so it shouldn't be Disposed independent of the X509Certificate2 object.
//
// The RSA base class has 'EncryptValue', but it just throws, so casting to
// RSACryptoServiceProvider is required.
//
// The PublicKey.Key property returns AsymmetricAlgorithm, so really this call should be
// done via 'as', or another safe flow... but it's almost always seen as an explicit cast
RSACryptoServiceProvider rsaCsp = (RSACryptoServiceProvider)cert.PublicKey.Key;
// RSACryptoServiceProvider.Encrypt's second parameter is a bool. While the documentation
// does say what it does, it doesn't tell someone familiar with the concepts (but not the
// particular implementation) what's going on.
return rsaCsp.Encrypt(data, true);
}
// 4.6:
public static byte[] EncryptDataOaepSha1(X509Certificate2 cert, byte[] data)
{
// GetRSAPublicKey returns an object with an independent lifetime, so it should be
// handled via a using statement.
using (RSA rsa = cert.GetRSAPublicKey())
{
// OAEP allows for multiple hashing algorithms, what was formerly just "OAEP" is
// now OAEP-SHA1.
return rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA1);
}
}
// Example 4: Encrypting a byte[] using OAEP-SHA256
// 4.5: Not possible
// 4.6:
public static byte[] EncryptDataOaepSha256(X509Certificate2 cert, byte[] data)
{
using (RSA rsa = cert.GetRSAPublicKey())
{
return rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment