RSA Cryptography and saving to database (c#) - c#

I don't have deep information about cryptography ,but as I gathered some information,as an asymmetric solution I want to use RSA and here is the code which I found,I want to store an encrypted password into database and decrypt it whenever I want to get the user information,I mean these two actions don't happen respectively and whenever I like I can select from database and decrypt the previously save data.I know that there is something wrong with the generated keys,but because of low knowledge on this subject I cannot handle it.
I get the following Exception on decryption.
The parameter is incorrect.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean fOAEP, ObjectHandleOnStack ohRetDecryptedKey)
at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
Encryption Class:
public static class AsymmetricEncryption
{
private static bool _optimalAsymmetricEncryptionPadding = false;
public static void GenerateKeys(int keySize, out string publicKey, out string publicAndPrivateKey)
{
using (var provider = new RSACryptoServiceProvider(keySize))
{
publicKey = provider.ToXmlString(false);
publicAndPrivateKey = provider.ToXmlString(true);
}
}
public static string EncryptText(string text, int keySize, string publicKeyXml)
{
var encrypted = Encrypt(Encoding.UTF8.GetBytes(text), keySize, publicKeyXml);
return Convert.ToBase64String(encrypted);
}
public static byte[] Encrypt(byte[] data, int keySize, string publicKeyXml)
{
if (data == null || data.Length == 0) throw new ArgumentException("Data are empty", "data");
int maxLength = GetMaxDataLength(keySize);
if (data.Length > maxLength) throw new ArgumentException(String.Format("Maximum data length is {0}", maxLength), "data");
if (!IsKeySizeValid(keySize)) throw new ArgumentException("Key size is not valid", "keySize");
if (String.IsNullOrEmpty(publicKeyXml)) throw new ArgumentException("Key is null or empty", "publicKeyXml");
using (var provider = new RSACryptoServiceProvider(keySize))
{
provider.FromXmlString(publicKeyXml);
return provider.Encrypt(data, _optimalAsymmetricEncryptionPadding);
}
}
public static string DecryptText(string text, int keySize, string publicAndPrivateKeyXml)
{
var decrypted = Decrypt(Convert.FromBase64String(text), keySize, publicAndPrivateKeyXml);
return Encoding.UTF8.GetString(decrypted);
}
public static byte[] Decrypt(byte[] data, int keySize, string publicAndPrivateKeyXml)
{
if (data == null || data.Length == 0) throw new ArgumentException("Data are empty", "data");
if (!IsKeySizeValid(keySize)) throw new ArgumentException("Key size is not valid", "keySize");
if (String.IsNullOrEmpty(publicAndPrivateKeyXml)) throw new ArgumentException("Key is null or empty", "publicAndPrivateKeyXml");
using (var provider = new RSACryptoServiceProvider(keySize))
{
provider.FromXmlString(publicAndPrivateKeyXml);
return provider.Decrypt(data, _optimalAsymmetricEncryptionPadding);
}
}
public static int GetMaxDataLength(int keySize)
{
if (_optimalAsymmetricEncryptionPadding)
{
return ((keySize - 384) / 8) + 7;
}
return ((keySize - 384) / 8) + 37;
}
public static bool IsKeySizeValid(int keySize)
{
return keySize >= 384 &&
keySize <= 16384 &&
keySize % 8 == 0;
}
}
Encrypt usage:
const int keySize = 1024;
string publicAndPrivateKey;
string publicKey;
AsymmetricEncryption.GenerateKeys(keySize, out publicKey, out publicAndPrivateKey);
string text = "text for encryption";
string encrypted = AsymmetricEncryption.EncryptText(text, keySize, publicKey);
Decrypt usage:
const int keySize = 1024;
string publicAndPrivateKey;
string publicKey;
AsymmetricEncryption.GenerateKeys(keySize, out publicKey, out publicAndPrivateKey);
string text = "text for encryption";
string encrypted = AsymmetricEncryption.DecryptText(text, keySize, publicKey);
I read the encrypted text from a table.And I like to decrypt it.
Please help me solve this issue.
Thanks in advance
Snippet link

Let me stop you right here, before you actually figure out the exception.
IT IS A HORRIBLE IDEA TO STORE PASSWORDS REVERSIBLY, regardless what encryption you use. There are memory scrubbers and a million other problems with that scheme.
Passwords are to be HASHED, NOT ENCRYPTED. The difference is that hashes are hard to reverse if implemented properly. Use BCrypt or SHA2 or SHA3 or some other good hashing algorithms, use proper salt.
Here's an example, but consult someone knowledgeable before using in production, I may have bugs, the methods are coded for simplicity and may be too simple:
public string GetHash(string secret, string salt = null)
{
var hasher = SHA256Managed.Create();
salt = salt ?? Guid.NewGuid().ToString("N"); // 128 bit salt
var hashResult = hasher.ComputeHash(Encoding.UTF8.GetBytes(salt+"|"+what));
return "SHA256|" + salt + "|" + Convert.ToBase64String(hashResult);
}
Now you can store the string you get from this function in your DB and the next time a password comes in (over HTTPS, I hope) you can check it by getting the salt from the string stored in DB:
public bool CheckSecretHash(string secret, string hashFromDB){
var hashParts = hashFromDB.Split('|'); // string[3]
var rehash = GetHash(secret, hashParts[1]);
return hashFromDB == rehash;
}
Bonus points: challenge-response. ALWAYS USE HTTPS.

Related

Why is “Padding is invalid and cannot be removed“ error thrown after disguising then reverting cipher, key and IV values

I’m encrypting sensitive data using the C# AES class, Aes.Create() method before saving to an SQLite DB. My base round trip is:
*Saving/retrieving each Byte[ ] without converting threw “The specified key is not a valid size for this algorithm.” when decrypting.
The process leverages the MSDN example and is working well:
I’m concerned, however, that cipher, key & IV values must all be stored/retrieved together and am attempting to disguise the values stored in the DB. I’ve tried three procedures to disguise/revert the relevant string values. Unfortunately adding a disguise/revert procedure throws the following error when decrypting:
System.Security.Cryptography.CryptographicException
HResult=0x80131430 Message=Padding is invalid and cannot be removed.
Source=System.Core
I've tried:
switching from Aes.Create to AesManaged; result: no change
setting Padding = PaddingMode.None; result: threw "'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll 'Length of the data to encrypt is invalid.'"
setting Padding = PaddingMode.Zeros; result: ran with no error but round trips returned incorrect & unreadable values (see image):
A number of posts (of the many, most of the issues are base process round trip failures) in this forum regarding this error state that the Padding error can be thrown for a variety of reasons only one of which is actually a Padding error. One post ( Padding is invalid and cannot be removed?, 4th reply sorted by Trending) states:
If the same key and initialization vector are used for encoding and
decoding, this issue does not come from data decoding but from data
encoding.
Since my keys & IVs are the same this looked hopeful. However, as the above image implies if I'm interpreting it correctly, changing encoding doesn't seem to help.
Since my base processs by itself works I suspect my case fall into the "not really a Padding error" category.
The disguise/revert procedures
First procedure
Remove the “=” character(s) at the end of each string since it’s a blatant indicator of “something important”
Split cipher, key & IV strings based on a random number into two halves
Swap the two halves’ positions and insert unique random text of random length between the halves
Create a random length “reversion code” capturing randomized split indices and resultant string lengths
Second procedure: same as the first but leave “=” character(s) intact.
Third procedure
Split the random text of three different random length strings
“Wrap” them around the cipher, key & IV strings
Create the “reversion code”
For all three procedures: upon retrieval from the DB reverse the process using the “reversion code”. In the third procedure the original strings should, theoretically, be “untouched”.
In all three cases output indicates Original and Reverted are identical (single run testing output sample):
The disguise/revert code
Code for the third procedure since it probably has the greatest probability of working. The Encrypt & Decrypt methods are included in case they're relevant:
private static string _cipherText = "", _keyText = "", _vectorText = "";
// Prior to saving: third procedure
public static void SaveSensitivePrep(string input, bool parent)
{
input = "secretID";
Console.WriteLine($"\nOriginal: {input}");
string startString = default, extractor = default;
int disguiseLen = 0, startStringLen = 0, doneStringLen = 0;
for (int i = 0; i < 3; i++)
{
using (Aes toEncrypt = Aes.Create())
{
byte[] cypherBytes = Encrypt(input, toEncrypt.Key, toEncrypt.IV);
string cipherText = Convert.ToBase64String(cypherBytes);
string keyText = Convert.ToBase64String(toEncrypt.Key);
string vectorText = Convert.ToBase64String(toEncrypt.IV);
if (i == 0) _cipherText = startString = cipherText;
else if (i == 1) _keyText = startString = keyText;
else if (i == 2) _vectorText = startString = vectorText;
int disguiseSelector = new Random().Next(101);
string disguiseTxt = default;
List<Disguises> disguise_DB = DBconnect.Disguises_Load();
for (int c = 0; c < disguise_DB.Count; c++)
{
if (c == disguiseSelector) { disguiseTxt = disguise_DB[c].Linkages; break; }
}
disguiseLen = disguiseTxt.Length;
int disguiseSplitIndex = new Random().Next(10, 22);
string startTxt = disguiseTxt.Substring(0, disguiseSplitIndex);
string endTxt = disguiseTxt.Substring(disguiseSplitIndex);
string doneString = $"{startTxt}{startString}{endTxt}";
doneStringLen = doneString.Length;
extractor = $"{disguiseSplitIndex}{disguiseLen}{startStringLen}" +
$"{disguiseSelector + new Random().Next(5000000)}";
if (i == 0) DBconnect.Encrypted_Update_CT(Host, extractor, doneString);
else if (i == 1) DBconnect.Encrypted_Update_CK(Host, extractor, doneString);
else if (i == 2) DBconnect.Encrypted_Update_CV(Host, extractor, doneString);
}
}
}
// After retrieval: third procedure
public static string LoadSensitive(string input, bool parent)
{
string cipherText = "", keyText = "", vectorText = "";
for (int i = 0; i < 3; i++)
{
string extractor = "";
int disguiseLeft, disguiseRight;
List<EncryptedClass> host = DBconnect.Encrypted_Load(Host);
string doneString = "";
if (i == 0) { extractor = host[0].RCC; doneString = host[0].cipherText; }
else if (i == 1) { extractor = host[0].RCK; doneString = host[0].keyText; }
else if (i == 2) { extractor = host[0].RCV; doneString = host[0].vectorText; }
string disguiseSplitter = extractor.Substring(0, 2);
string disguiseLen_t = extractor.Substring(2, 2);
string startStringLen_t = extractor.Substring(4, 2);
int.TryParse(disguiseSplitter, out int indx);
int.TryParse(disguiseLen_t, out int disguiseLen);
int.TryParse(startStringLen_t, out int startStringLen);
disguiseRight = disguiseLen - indx;
disguiseLeft = disguiseLen - disguiseRight;
string e_extrctd = doneString.Substring(disguiseLeft, startStringLen);
if (i == 0) cipherTxt = revertedString;
else if (i == 1) keyTxt = revertedString;
else vectorTxt = revertedString;
}
byte[] cypher = Convert.FromBase64String(cipherText);
byte[] key = Convert.FromBase64String(keyText);
byte[] vector = Convert.FromBase64String(vectorText);
return Decrypt(cypher, key, vector);
}
static byte[] Encrypt(string input, byte[] key, byte[] iv)
{
// NULL/Length > 0 checks.
byte[] encrypted;
using (Aes input_e = Aes.Create())
{
input_e.Key = key;
input_e.IV = iv;
ICryptoTransform ict = input_e.CreateEncryptor(input_e.Key, input_e.IV);
using (MemoryStream msInput = new MemoryStream())
{
using (CryptoStream csInput = new CryptoStream(msInput, ict, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csInput))
{
swEncrypt.Write(input);
}
encrypted = msInput.ToArray();
}
}
}
return encrypted;
}
static string Decrypt(byte[] cipherText, byte[] keyText, byte[] vectorText)
{
// NULL/Length > 0 checks.
string roundtrip = null;
using (Aes output_e = Aes.Create())
{
output_e.Key = keyText;
output_e.IV = vectorText;
ICryptoTransform ict = output_e.CreateDecryptor(output_e.Key, output_e.IV);
using (MemoryStream msOutput = new MemoryStream(cipherText))
{
using (CryptoStream csOutput = new CryptoStream(msOutput, ict, CryptoStreamMode.Read))
{
using (StreamReader srOutput = new StreamReader(csOutput))
{
roundtrip = srOutput.ReadToEnd();
}
}
}
}
return roundtrip;
}
Any guidance on how/why the disguise/revert is getting deformed will be appreciated.
EDIT: scope clarification: the security functionality in this instance is for a Winforms/SQLite app which has an FTP component (WinSCP). The app's high level objective is a desktop tool streamlining TLS/SSL Certificate request/issuance via Let's Encrypt. The primary security objective is protecting FTP session credentials should an unauthorized & semi-knowledgeable person gain access to the DB. The secondary security objective is to keep it as simple as possible.

What should be the signing key for the AWS HTTP Post example?

I am trying to walk through the sample code using the values in the "Example: Browser-Based Upload using HTTP POST (Using AWS Signature Version 4)" page, but could not get the same signature value posted on the page.
The page does not show the value for the signing key. Does anybody know what it supposed to be? Thanks.
namespace TestAWSSignature
{
class Program
{
// Values from aws example page.
const string aKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
const string anID = "AKIAIOSFODNN7EXAMPLE";
const string aRegion = "us-east-1";
const string aService = "s3";
const string aBucket = "sigv4examplebucket";
const string HMACSHA256 = "HMACSHA256";
const string aDate = "20151229";
const string SCHEME = "AWS4";
const string ALGORITHM = "HMAC-SHA256";
const string TERMINATOR = "aws4_request";
static void Main(string[] args)
{
// Initial to value from aws example page.
string base64PolicyString = "eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9";
byte[] policyStringBytes = Convert.FromBase64String(base64PolicyString);
// compute the signing key
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(HMACSHA256);
kha.Key = DeriveSigningKey(HMACSHA256, aKey, aRegion, aDate, aService);
Console.WriteLine(String.Format("Signing Key: {0}.\n", System.Convert.ToBase64String(kha.Key)));
// SigningKey value is: y87x6+rvyCzOZTC58KmuWYhGBl9cW64GdL1evEulLSg=
// Is this the correct value?
// Compute the signature.
byte[] signature = kha.ComputeHash(policyStringBytes);
string signatureString = ToHexString(signature, true);
Console.WriteLine(String.Format("Signature: {0}.\n", signatureString));
// The computed signature value is:
// 00e98ae3199cdbfeba701f9efa66510f23f0295ab6d6f4d14202f8ef2d11956c
// But according to the aws example page it should be:
// 8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e
Console.WriteLine("Done.");
}
// Functions below are straight from AWSSignatureV4-S3-Sample code.
/// Compute and return the multi-stage signing key for the request.
static byte[] DeriveSigningKey(string algorithm, string awsSecretAccessKey, string region, string date, string service)
{
const string ksecretPrefix = SCHEME;
char[] ksecret = null;
ksecret = (ksecretPrefix + awsSecretAccessKey).ToCharArray();
byte[] hashDate = ComputeKeyedHash(algorithm, Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date));
byte[] hashRegion = ComputeKeyedHash(algorithm, hashDate, Encoding.UTF8.GetBytes(region));
byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(service));
return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(TERMINATOR));
}
/// Compute and return the hash of a data blob using the specified algorithm and key
static byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data)
{
var kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(data);
}
/// Helper to format a byte array into string
static string ToHexString(byte[] data, bool lowercase)
{
var sb = new StringBuilder();
for (var i = 0; i < data.Length; i++)
{
sb.Append(data[i].ToString(lowercase ? "x2" : "X2"));
}
return sb.ToString();
}
}
}

How to make sure certificate is not changed?

There is an issue came into my mind.
Is there any common way to make an app running only when there is an installed certificate in the system. And I want such a certificate to be issued and verified by my self-signed certificate?
I can get a certificate by it's name from the storage, but how do I make sure such a certificate is signed by my self-signed certificate and nobody have issued a certificate with the same name and replaced the one in the local storage?
Or in other words, how do I make sure the certificate which signed the certificate at local storage is not a forged one?
I'm sorry if its not correct and|or clear question, but I'll be happy to have help regarding it.
Very good question indeed.
There is always a possibility of the end-user creating a valid certificate chain with your subject name and as the issuer, another for the issuer ceritificate, all up to the root.
What they canot do is to sign those certifcates with the issuer certificate's private key.
Therefore, the code below loads the application certificate from the personal certificate store of the current user, then, loads the issuer certificate of the issuer from the resources and verifies the signature on the application certificate installed on the client machine using the public key of the issuer certificate.
In my source code, the issuer certificate is added to the resources with the key IssuerCertificate
I am actually fond of coming out with a solution like this.
In the code I mention an encoding ASN.1. Check it here if you need
static void Main(string[] args)
{
string expectedSubjectName = "My Application";
X509Certificate2 issuerCertificate = new X509Certificate2(Resource1.IssuerCertificate);
string expectedIssuerName = issuerCertificate.Subject;
bool result = VerifyCertificateIssuer(expectedSubjectName, expectedIssuerName, issuerCertificate);
}
private static void ThrowCertificateNotFoundException(string expectedSubjectName, string expectedIssuerName, bool isThumbprintMismatch)
{
if (isThumbprintMismatch)
{
// Notification for possible certificate forgery
}
throw new SecurityException("A certificate with subject name " + expectedSubjectName + " issued by " + expectedIssuerName + " is required to run this application");
}
private static X509Certificate2 GetCertificate(string expectedSubjectName, string expectedIssuerName)
{
X509Store personalCertificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
personalCertificateStore.Open(OpenFlags.ReadOnly);
X509CertificateCollection certificatesBySubjectName = personalCertificateStore.Certificates.Find(X509FindType.FindBySubjectName, expectedSubjectName, true);
if (certificatesBySubjectName.Count == 0)
{
ThrowCertificateNotFoundException(expectedSubjectName, expectedIssuerName, false);
}
X509Certificate2 matchingCertificate = null;
foreach (X509Certificate2 certificateBySubjectName in certificatesBySubjectName)
{
if (certificateBySubjectName.Issuer == expectedIssuerName)
{
matchingCertificate = certificateBySubjectName;
break;
}
}
if (matchingCertificate == null)
{
ThrowCertificateNotFoundException(expectedSubjectName, expectedIssuerName, false);
}
return matchingCertificate;
}
private static bool VerifyCertificateIssuer(string expectedSubjectName, string expectedIssuerName, X509Certificate2 issuerCertificate)
{
X509Certificate2 matchingCertificate = GetCertificate(expectedSubjectName, expectedIssuerName);
X509Chain chain = new X509Chain();
chain.Build(matchingCertificate);
// bool x = Encoding.ASCII.GetString(chain.ChainElements[1].Certificate.RawData) == Encoding.ASCII.GetString(issuerCertificate.RawData);
byte[] certificateData = matchingCertificate.RawData;
MemoryStream asn1Stream = new MemoryStream(certificateData);
BinaryReader asn1StreamReader = new BinaryReader(asn1Stream);
// The der encoded certificate structure is like this:
// Root Sequence
// Sequence (Certificate Content)
// Sequence (Signature Algorithm (like SHA256withRSAEncryption)
// Sequence (Signature)
// We need to decode the ASN.1 content to get
// Sequence 0 (which is the actual certificate content that is signed by the issuer, including the sequence definition and tag number and length)
// Sequence 2 (which is the signature. X509Certificate2 class does not give us that information. The year is 2015)
// Read the root sequence (ignore)
byte leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
ReadDataLength(asn1StreamReader);
// Save the current position because we will need it for including the sequence header with the certificate content
int sequence0StartPosition = (int)asn1Stream.Position;
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence0ContentLength = ReadDataLength(asn1StreamReader);
int sequence0HeaderLength = (int)asn1Stream.Position - sequence0StartPosition;
sequence0ContentLength += sequence0HeaderLength;
byte[] sequence0Content = new byte[sequence0ContentLength];
asn1Stream.Position -= 4;
asn1StreamReader.Read(sequence0Content, 0, sequence0ContentLength);
// Skip sequence 1 (signature algorithm) since we don't need it and assume that we know it because we own the issuer certificate
// This sequence, containing the algorithm used during the signing process IS HIDDEN FROM US BY DEFAULT. The year is 2015.
// What should have been done for real is, to get the algorithm ID (hash algorithm and asymmetric algorithm) and to use those
// algorithms during the verification process
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence1ContentLength = ReadDataLength(asn1StreamReader);
byte[] sequence1Content = new byte[sequence1ContentLength];
asn1StreamReader.Read(sequence1Content, 0, sequence1ContentLength);
// Read sequence 2 (signature)
// The actual signature of the certificate IS HIDDEN FROM US BY DEFAULT. The year is 2015.
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence2ContentLength = ReadDataLength(asn1StreamReader);
byte unusedBits = asn1StreamReader.ReadByte();
sequence2ContentLength -= 1;
byte[] sequence2Content = new byte[sequence2ContentLength];
asn1StreamReader.Read(sequence2Content, 0, sequence2ContentLength);
// At last, we have the data that is signed and the signature.
bool verificationResult = ((RSACryptoServiceProvider)issuerCertificate.PublicKey.Key)
.VerifyData
(
sequence0Content,
CryptoConfig.MapNameToOID("SHA256"),
sequence2Content
);
return verificationResult;
}
private static byte[] ReadTagNumber(byte leadingOctet, BinaryReader inputStreamReader)
{
List<byte> byts = new List<byte>();
byte temporaryByte;
if ((leadingOctet & 0x1F) == 0x1F) // More than 1 byte is used to specify the tag number
{
while (((temporaryByte = inputStreamReader.ReadByte()) & 0x80) > 0)
{
byts.Add((byte)(temporaryByte & 0x7F));
}
byts.Add(temporaryByte);
}
else
{
byts.Add((byte)(leadingOctet & 0x1F));
}
return byts.ToArray();
}
private static int ReadDataLength(BinaryReader inputStreamReader)
{
byte leadingOctet = inputStreamReader.ReadByte();
if ((leadingOctet & 0x80) > 0)
{
int subsequentialOctetsCount = leadingOctet & 0x7F;
int length = 0;
for (int i = 0; i < subsequentialOctetsCount; i++)
{
length <<= 8;
length += inputStreamReader.ReadByte();
}
return length;
}
else
{
return leadingOctet;
}
}
private static byte[] GetTagNumber(byte leadingOctet, BinaryReader inputStreamReader, ref int readBytes)
{
List<byte> byts = new List<byte>();
byte temporaryByte;
if ((leadingOctet & 0x1F) == 0x1F) // More than 1 byte is used to specify the tag number
{
while (((temporaryByte = inputStreamReader.ReadByte()) & 0x80) > 0)
{
readBytes++;
byts.Add((byte)(temporaryByte & 0x7F));
}
byts.Add(temporaryByte);
}
else
{
byts.Add((byte)(leadingOctet & 0x1F));
}
return byts.ToArray();
}

C# and ASP.NET: Error occurred while decoding OAEP padding

I've tried everything I found cross the web, but I couldn't figure out what is the problem. I have these methods to encrypt/decrypt strings:
public static class SecurityUtils {
public static readonly string EncryptionKey = "my.key." + Guid.NewGuid();
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]
public static string Encrypt(this string stringToEncrypt, string key) {
if (string.IsNullOrEmpty(stringToEncrypt)) {
throw new ArgumentException("An empty string value cannot be encrypted.");
}
if (string.IsNullOrEmpty(key)) {
throw new ArgumentException("Cannot encrypt using an empty key. Please supply an encryption key.");
}
var cspp = new CspParameters { KeyContainerName = key, Flags = CspProviderFlags.UseMachineKeyStore };
var rsa = new RSACryptoServiceProvider(cspp) { PersistKeyInCsp = true };
byte[] bytes = rsa.Encrypt(System.Text.Encoding.UTF8.GetBytes(stringToEncrypt), true);
return BitConverter.ToString(bytes);
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]
public static string Decrypt(this string stringToDecrypt, string key) {
if (string.IsNullOrEmpty(stringToDecrypt)) {
throw new ArgumentException("An empty string value cannot be encrypted.");
}
if (string.IsNullOrEmpty(key)) {
throw new ArgumentException("Cannot decrypt using an empty key. Please supply a decryption key.");
}
var cspp = new CspParameters { KeyContainerName = key, Flags = CspProviderFlags.UseMachineKeyStore };
var rsa = new RSACryptoServiceProvider(cspp) { PersistKeyInCsp = true };
var decryptArray = stringToDecrypt.Split(new[] { "-" }, StringSplitOptions.None);
var decryptByteArray = Array.ConvertAll(decryptArray, (s => Convert.ToByte(byte.Parse(s, System.Globalization.NumberStyles.HexNumber))));
byte[] bytes = rsa.Decrypt(decryptByteArray, true);
string result = System.Text.Encoding.UTF8.GetString(bytes);
return result;
}
}
I'm using this code for months in several projects and it seems perfect. Until a project I'm working on now. It works local just as I want to. But when I publish it to my server, I get this error:
Error occurred while decoding OAEP padding.
Stack Trace:
[CryptographicException: Error occurred while decoding OAEP padding.]
System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean fOAEP, ObjectHandleOnStack ohRetDecryptedKey) +0
System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP) +214
myNamespace.SecurityUtils.Decrypt(String stringToDecrypt, String key) +260
... the rest...
Do you have any idea please? Thanks in advance.

HashPasswordForStoringInConfigFile - Different Hashes for same password

I have recently implemented Hashing for my passwords in a project I am working on, and I cant seem to figure out what is going wrong.
It seems that the HashPasswordForStoringInConfigFile() function is returning different values for the same password.
I have the following code implemented which actually closely resembles the recommended algorithm to use on the MSDN documentation.
I know that SHA1 hashing is not considered very safe, but this is for a research application, and I am not too worried about it at this point.
public const int DefaultSaltSize = 5;
private static string CreateSalt()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buffer = new byte[DefaultSaltSize];
rng.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
public static string CreateHash(string password)
{
string salt = CreateSalt();
string saltAndPassword = String.Concat(password, salt);
string hashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPassword,"SHA1");
hashedPassword = string.Concat(hashedPassword,salt);
return hashedPassword;
}
public static bool VerifyPassword(string username, string password,AccountDataContext context)
{
var user = context.UserAccounts.FirstOrDefault(p => p.UserName == username);
if (user != null)
{
string salt = user.Password.Substring(user.Password.Length - DefaultSaltSize);
string hashedPassword = CreateHash(password);
return hashedPassword.Equals(user.Password);
}
return false;
}
Simply put, If I have the following code.
string password1 = "password";
string password2 = "password";
var hashedPassword1 = CreateHash(password1);
var hashedPassword2 = CreateHash(password2);
var match = hashedPassword1.Equals(hashedPassword2);
//match should be True, but it is turning out False.
It seems that the FormsAuthenticationForStoringInConfigFile() is not returning the same hash for password1 and password2 in the CreateHash() method.
I understand with the salt applied they are not the same, but if you see in the code, I am removing the salt before comparing the two hashedPasswords for equality.
What could possibly be causing password1 and password2 from being hashed differently?
Your code has added salt (a random value) to the password before hashing. This is a good thing.
It means that if user A and user B use the same password, the password hashes will nevertheless be different.
Your VerifyPassword method is not using the original salt to hash the password for comparing - instead it calls CreateHash, which calls CreateSalt and creates new salt.
You might try something like:
public static string CreateHash(string password)
{
return CreateHash(password, CreateSalt());
}
private static string CreateHash(string password, string salt)
{
string saltAndPassword = String.Concat(password, salt);
string hashedPassword =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPassword,"SHA1");
hashedPassword = string.Concat(hashedPassword,salt);
return hashedPassword;
}
public static bool VerifyPassword(string username,
string password,AccountDataContext context)
{
var user = context.UserAccounts.FirstOrDefault(p => p.UserName == username);
if (user != null)
{
string salt = user.Password.Substring(user.Password.Length - DefaultSaltSize);
string hashedPassword = CreateHash(password, salt);
return hashedPassword.Equals(user.Password);
}
return false;
}
Even though VerifyPassword looks like it's stripping off the salt portion of the unhashed string, but the code you say should return true doesn't actually call VerifyPassword.
Your code simply generates two salted hashes and then uses String.Equals to compare them.
What happens when you use VerifyPassword instead of String.Equals?
This code doesn't work at all either. Why is it marked as being the correct answer?
The default length of Salt is set to 5
Create Salt when it takes a 5 byte array to a string it becomes 8 characters not 5
Verify Password then takes only 5 characters off for the salt not 8 so the verify will always fail as it's using 5 characters for the salt and not the 8 that was used to create the hashed password.
Below is updated code to make the above code work.
private const int DEFAULT_SALT_SIZE = 5;
private static string CreateSalt()
{
RNGCryptoServiceProvider rngCryptoServiceProvider = new RNGCryptoServiceProvider();
byte[] buffer = new byte[DEFAULT_SALT_SIZE];
rngCryptoServiceProvider.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
public static string CreateHash(string password)
{
return CreateHash(password, CreateSalt());
}
private static string CreateHash(string password, string salt)
{
string saltAndPassword = String.Concat(password, salt);
string hashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPassword, "SHA1");
hashedPassword = string.Concat(hashedPassword, salt);
return hashedPassword;
}
public static bool VerifyPassword(string userpassword, string password)
{
byte[] bytePassword = Convert.FromBase64String(userpassword);
byte[] byteSalt = new byte[DEFAULT_SALT_SIZE];
Array.Copy(bytePassword, bytePassword.Length - DEFAULT_SALT_SIZE, byteSalt, 0, DEFAULT_SALT_SIZE);
string salt = Convert.ToBase64String(byteSalt);
string hashedPassword = CreateHash(password, salt);
return hashedPassword.Equals(userpassword);
}
This is how I called it.
string hashedPassword = Security.CreateHash("password");
if (Security.VerifyPassword(hashedPassword, "password"))
{
Response.Write("Valid");
}
else
{
Response.Write("Not Valid");
}
As long as the passwords match it returns true otherwise it will return false.
This gives you what I think was intended which was not only to include the salt inside of the hash but also add it to the outside of the hash so it all could be stored as 1 column value in a database and then used to recreate the hash on the user entered password using the stored salt value and get a match.

Categories