Below is my code and error is in the Title. I encrypted using private key but I decrypt using the same private key I ran into the error mentioned in the title, not really sure what is the underlying cause of the error. Any help in that regard will be highly appreciated.
Class Program
{
static void Main(string[] args)
{
string thumbPrint = "SomeValue";
EncryptUserName("Steve", thumbPrint, true, true);
}
public static void EncryptUserName(string textToEncript, string certificateThumbprint, bool searchLocalMachine, bool searchUser)
{
X509Certificate2 cert = FindCertificate(certificateThumbprint, searchLocalMachine, searchUser);
RSACryptoServiceProvider rsaEncryptor = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] cipherData = rsaEncryptor.Encrypt(Encoding.UTF8.GetBytes(textToEncript), true);
var encryptedString = Convert.ToBase64String(cipherData);
Console.WriteLine(encryptedString);
}
public static byte[] Decrypt(byte[] encryptedData, bool fOAEP, X509Certificate2 certificate)
{
if (encryptedData == null)
{
throw new ArgumentNullException("encryptedData");
}
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
if (certificate.PrivateKey == null)
{
throw new ApplicationException("Certificate provided has no private key");
}
Console.WriteLine(certificate.PrivateKey);
using (RSACryptoServiceProvider provider = (RSACryptoServiceProvider)certificate.PrivateKey)
{
return provider.Decrypt(encryptedData, fOAEP);
}
}
public static string CertificateDecrypt(string textToDecript, string certificateThumbprint, bool searchLocalMachine, bool searchUser)
{
X509Certificate2 certificate = FindCertificate(certificateThumbprint, searchLocalMachine, searchUser);
byte[] bytesArray = Convert.FromBase64String(textToDecript);
//Decrypt(bytesArray, true, certificate);
return Encoding.UTF8.GetString(Decrypt(bytesArray, true, certificate));
}
public static X509Certificate2 LoadCertificate(StoreName storeName, StoreLocation storeLocation, string thumbprint)
{
X509Store store = null;
X509Certificate2 certificate2;
try
{
store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Enumerator enumerator = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false).GetEnumerator();
X509Certificate2 current = null;
while (enumerator.MoveNext())
{
current = enumerator.Current;
}
certificate2 = current;
}
finally
{
if (store != null)
{
store.Close();
}
}
return certificate2;
}
private static X509Certificate2 FindCertificate(string certificateThumbprint, bool searchLocalMachine, bool searchUser)
{
certificateThumbprint = certificateThumbprint.Replace(" ", "");
X509Certificate2 certificate = null;
if (searchUser)
{
certificate = LoadCertificate(StoreName.My, StoreLocation.CurrentUser, certificateThumbprint);
}
if (searchLocalMachine && (certificate == null))
{
certificate = LoadCertificate(StoreName.My, StoreLocation.LocalMachine, certificateThumbprint);
}
if (certificate == null)
{
throw new ApplicationException($"Certificate with thumbprint {certificateThumbprint} cannot be loaded (not found)");
}
return certificate;
}
}
Error occurred while decoding OAEP Padding happens when either the input string for the decrypt is not exactly the same as the output string of the encrypt, or because the public key used for encryption does not match the private key used for decryption.
In your case, you are not actually passing an encrypted string to the Decrypt method so it will absolutely always fail, essentially making the encryption and decryption key not match.
Related
I'm trying to verify some data with a public key the method ecdsa.VerifyData() that I'm signing with private key and I'm getting a false return and I don't know why.
These are the methods to Sign and Verify the data:
public byte[] SignData(byte[] dataValue)
{
X509Certificate2 privateKey = new X509Certificate2(privateKeyPfxFile);
//Encryting/Signing a hash
using (ECDsa ecdsa = privateKey.GetECDsaPrivateKey())
{
if (ecdsa == null) throw new Exception("Not an ECDSA cert, or has no private key");
return ecdsa.SignData(dataValue, HashAlgorithmName.SHA256);
}
}
public bool VerifyData(byte[] dataValue, byte[] dataSigned)
{
byte[] mycertCer = Properties.Resources.mycertCer;
X509Certificate2 publicKey = new X509Certificate2(mycertCer);
//Checking the hash and signature
using (ECDsa ecdsa = publicKey.GetECDsaPublicKey())
{
if (ecdsa == null) throw new Exception("Not an ECDSA cert, or has no private key");
return ecdsa.VerifyData(dataValue, dataSigned, HashAlgorithmName.SHA256);
}
}
This is how I create the keys:
public void CreateAsymmetricKeysPair(string path, string keyName)
{
var ecdsa = ECDsa.Create(); // generate asymmetric key pair
var req = new CertificateRequest("cn=localhost", ecdsa, HashAlgorithmName.SHA256);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
// Create PFX (PKCS #12) with private key
File.WriteAllBytes(Path.Join(path, keyName + ".pfx"), cert.Export(X509ContentType.Pfx));
// Create Base 64 encoded CER (public key only)
File.WriteAllText(Path.Join(path,keyName + ".cer"),
"-----BEGIN CERTIFICATE-----\r\n"
+ Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
+ "\r\n-----END CERTIFICATE-----");
}
That is my unit test that I'm getting a false return:
private const string rawText = "test";
[Fact]
public void OnSignData_DoReturnSignature_WhenValidStream()
{
//Arrange
var rawBytes = Encoding.UTF8.GetBytes(rawText);
//Act
var signature = licenseService.SignData(rawBytes);
var isValid = licenseService.VerifyData(rawBytes, signature);
//Assert
Assert.True(isValid);
}
Am I missing something?
I just checked that this line was returning my old public key even that I deleted and pasted another one.
byte[] mycertCer = Properties.Resources.mycertCer;
I have recieved a public keY in txt format. (BEGIN CERTIFICATE---END CERTIFICATE)
I want to encrypt my message using this key in C# and send it across.
Similarly I have my private key in text format. I have shared my public key with the third party, which they are using to encrypt the message. I want to decrypt the message using my private key in TEXT format.
How do i do that in C#?
Kindly help.
public class MyCrypto
{
public X509Certificate2 GetDecryptionCertificate(string certificateName)
{
var my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
var collection = my.Certificates.Find(X509FindType.FindBySubjectName, certificateName, false);
if (collection.Count == 1)
{
return collection[0];
}
else if (collection.Count > 1)
{
throw new Exception(string.Format("More than one certificate with name '{0}' found in store LocalMachine/My.", certificateName));
}
else
{
throw new Exception(string.Format("Certificate '{0}' not found in store LocalMachine/My.", certificateName));
}
}
public X509Certificate2 GetEncryptionCertificate(string filePath)
{
var collection = new X509Certificate2Collection();
collection.Import(filePath);
return collection[0];
}
public string EncryptRsa(string input, X509Certificate2 x509Certificate2)
{
var output = string.Empty;
using (RSA csp = (RSA)x509Certificate2.PublicKey.Key)
{
byte[] bytesData = Encoding.UTF8.GetBytes(input);
byte[] bytesEncrypted = csp.Encrypt(bytesData, RSAEncryptionPadding.OaepSHA1);
output = Convert.ToBase64String(bytesEncrypted);
}
return output;
}
public string DecryptRsa(string encrypted, X509Certificate2 x509Certificate2)
{
var text = string.Empty;
using (RSA csp = (RSA)x509Certificate2.PrivateKey)
{
byte[] bytesEncrypted = Convert.FromBase64String(encrypted);
byte[] bytesDecrypted = csp.Decrypt(bytesEncrypted, RSAEncryptionPadding.OaepSHA1);
text = Encoding.UTF8.GetString(bytesDecrypted);
}
return text;
}
}
I know that PKCS#7 = Certificate + Optional raw data + Signature in PKCS#1 format I need to extract PKCS#1 from a PKCS#7 signature how can I do this in C#. Can I use the bouncy castle to do this, Here is my implementation
ie. to convert PKCS#7 to ASN.1 and to take the last sequence as it is PKCS#1
Asn1InputStream asn1 = new Asn1InputStream(pkcs7Stream);
Asn1Sequence sequence = (Asn1Sequence)asn1.ReadObject().ToAsn1Object();
var sequenceString = sequence.ToString();
var lastCommaIndex = sequenceString.LastIndexOf(",");
var pkcs1HexStr = sequenceString.Substring(lastCommaIndex + 3).Replace("]", string.Empty);
Is there any other eligant way to obtain PKCS#1
The SignedCms class can do this for you, .NET Core 2.1+ or .NET Framework 4.7.2+:
SignedCms cms = new SignedCms();
cms.Decode(message);
return cms.SignerInfos[0].GetSignature();
Assuming you want the signature from the first signer, of course. (The GetSignature method is what requires net472+)
Other signers or countersigners would also be available, just through different aspects of the object model.
Thanks, #bartonis for the help and guidance
Here is implementation using bouncy castle
public static byte[] GetRaw(byte[] input)
{
SignerInfo signerInfo = GetSignerInfo(input);
return signerInfo?.EncryptedDigest?.GetOctets();
}
private static SignerInfo GetSignerInfo(byte[] input)
{
Asn1InputStream cmsInputStream = new Asn1InputStream(input);
Asn1Object asn1Object = cmsInputStream.ReadObject();
Asn1Sequence asn1Sequence = Asn1Sequence.GetInstance(asn1Object);
SignedData signedData = GetSignedData(asn1Sequence);
SignerInfo signerInfo = GetSignerInfo(signedData);
if (signerInfo?.UnauthenticatedAttributes != null)
{
signedData = GetSignerInfo(signerInfo);
signerInfo = GetSignerInfo(signedData);
}
return signerInfo;
}
private static SignerInfo GetSignerInfo(SignedData signedData)
{
Asn1Encodable[] Asn1Encodables = signedData?.SignerInfos?.ToArray();
if (Asn1Encodables != null)
{
if (Asn1Encodables.Length > 0)
{
SignerInfo signerInfo = SignerInfo.GetInstance(Asn1Encodables[0]);
return signerInfo;
}
}
return null;
}
private static SignedData GetSignedData(Asn1Sequence sequence)
{
var rootContent = ContentInfo.GetInstance(sequence);
var signedData = SignedData.GetInstance(rootContent.Content);
return signedData;
}
private static SignedData GetSignerInfo(SignerInfo signerInfo)
{
Asn1Encodable[] asn1Encodables = signerInfo.UnauthenticatedAttributes.ToArray();
foreach (var asn1Encodable in asn1Encodables)
{
Asn1Sequence sequence = Asn1Sequence.GetInstance(asn1Encodable);
DerObjectIdentifier OID = (DerObjectIdentifier)sequence[0];
if (OID.Id == "1.2.840.113549.1.9.16.2.14")
{
Asn1Sequence newSequence =Asn1Sequence.GetInstance(Asn1Set.GetInstance(sequence[1])[0]);
SignedData signedData = GetSignedData(newSequence);
return signedData;
}
}
return null;
}
I need to securely persist AES key(s) to be used by the .NET AesCng algorithm. The idea is to use the CngKey class to persist the key(s) and leverage its export/import functionality to maintain the same key(s) across multiple servers.
I can create the persisted AES key
public static bool CreateContainer(string name)
{
if (CngKey.Exists(name))
{
return false;
}
CngKeyCreationParameters keyCreationParameters = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey
};
CngKey cngKey = CngKey.Create(new CngAlgorithm("AES"), name, keyCreationParameters);
cngKey.Dispose();
return true;
}
and then use it to encrypt/decrypt
public static byte[] Encrypt(string keyContainerName, byte[] clearText, byte[] iv)
{
AesCng aesCng = null;
ICryptoTransform crypto = null;
byte[] cipher = null;
try
{
aesCng = new AesCng(keyContainerName);
aesCng.IV = (iv == null ? new byte[aesCng.IV.Length] : iv);
crypto = aesCng.CreateEncryptor();
cipher = crypto.TransformFinalBlock(clearText, 0, clearText.Length);
}
finally
{
if (crypto != null)
{
crypto.Dispose();
}
if (aesCng != null)
{
aesCng.Clear();
aesCng.Dispose();
}
}
return cipher;
}
public static byte[] Decrypt(string keyContainerName, byte[] cipher, byte[] iv)
{
AesCng aesCng = null;
ICryptoTransform crypto = null;
byte[] clearText = null;
try
{
aesCng = new AesCng(keyContainerName);
aesCng.IV = (iv == null ? new byte[aesCng.IV.Length] : iv);
crypto = aesCng.CreateDecryptor();
clearText = crypto.TransformFinalBlock(cipher, 0, cipher.Length);
}
finally
{
if (crypto != null)
{
crypto.Dispose();
}
if (aesCng != null)
{
aesCng.Clear();
aesCng.Dispose();
}
}
return clearText;
}
I am able to export the key
public static bool ExportKey(string name, out byte[] blob)
{
blob = null;
if (!CngKey.Exists(name))
{
return false;
}
CngKey cngKey = CngKey.Open(name);
blob = cngKey.Export(CngKeyBlobFormat.OpaqueTransportBlob);
cngKey.Dispose();
return true;
}
However, when I try to import the blob, I get a CryptographicException: The supplied handle is invalid.
public static void ImportKey(string name, byte[] blob)
{
CngKey cngKey = CngKey.Import(blob, CngKeyBlobFormat.OpaqueTransportBlob);
cngKey.Dispose();
}
I am at a loss to explain why the failure.
Can anyone shed some light on this?
Thanks.
I recently posted about issues with encrypting large data with RSA, I am finally done with that and now I am moving on to implementing signing with a user's private key and verifying with the corresponding public key. However, whenever I compare the signed data and the original message I basically just get false returned. I am hoping some of your could see what I am doing wrong.
Here is the code:
public static string SignData(string message, RSAParameters privateKey)
{
//// The array to store the signed message in bytes
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
//// Write the message to a byte array using UTF8 as the encoding.
var encoder = new UTF8Encoding();
byte[] originalData = encoder.GetBytes(message);
try
{
//// Import the private key used for signing the message
rsa.ImportParameters(privateKey);
//// Sign the data, using SHA512 as the hashing algorithm
signedBytes = rsa.SignData(originalData, CryptoConfig.MapNameToOID("SHA512"));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
finally
{
//// Set the keycontainer to be cleared when rsa is garbage collected.
rsa.PersistKeyInCsp = false;
}
}
//// Convert the a base64 string before returning
return Convert.ToBase64String(signedBytes);
}
So that is the first step, to sign the data, next I move on to verifying the data:
public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
byte[] signedBytes = Convert.FromBase64String(signedMessage);
try
{
rsa.ImportParameters(publicKey);
SHA512Managed Hash = new SHA512Managed();
byte[] hashedData = Hash.ComputeHash(signedBytes);
success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signedBytes);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
return success;
}
And here is the test client:
public static void Main(string[] args)
{
PublicKeyInfrastructure pki = new PublicKeyInfrastructure();
Cryptograph crypto = new Cryptograph();
RSAParameters privateKey = crypto.GenerateKeys("email#email.com");
const string PlainText = "This is really sent by me, really!";
RSAParameters publicKey = crypto.GetPublicKey("email#email.com");
string encryptedText = Cryptograph.Encrypt(PlainText, publicKey);
Console.WriteLine("This is the encrypted Text:" + "\n " + encryptedText);
string decryptedText = Cryptograph.Decrypt(encryptedText, privateKey);
Console.WriteLine("This is the decrypted text: " + decryptedText);
string messageToSign = encryptedText;
string signedMessage = Cryptograph.SignData(messageToSign, privateKey);
//// Is this message really, really, REALLY sent by me?
bool success = Cryptograph.VerifyData(messageToSign, signedMessage, publicKey);
Console.WriteLine("Is this message really, really, REALLY sent by me? " + success);
}
Am I missing a step here? According to the Cryptography API and the examples there, I shouldn't manually compute any hashes, since I supply the algorithm within the method call itself.
Any help will be greatly appreciated.
Your problem is at the beginning of the VerifyData method:
public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
//Don't do this, do the same as you did in SignData:
//byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes(originalMessage);
byte[] signedBytes = Convert.FromBase64String(signedMessage);
try
...
For some reason you switched to FromBase64String instead of UTF8Encoding.GetBytes.