System.Security.Cryptography and "Bad Data" - character encoding? - c#

The class that produces "Bad Data" errors:
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
namespace MyNameSpace
{
public class RSAcrypt
{
private string _encryptedData;
private string _decryptedData;
public string EncryptedData
{
get { return _encryptedData; }
set { _encryptedData = value; }
}
public string DecryptedData
{
get { return _decryptedData; }
set { _decryptedData = value; }
}
public RSAcrypt()
{
}
/// <param name="CryptAction"> The action to perform on the string {Encrypt|Decrypt} </param >
/// <param name="StringToCrypt"> A string to perform the Action on </param>
public RSAcrypt(string CryptAction, string StringToCrypt)
{
UnicodeEncoding thisUnicodeEncoding = new UnicodeEncoding();
RSACryptoServiceProvider thisRSACryptoServiceProvider = new RSACryptoServiceProvider();
byte[] _stringToCrypt = thisUnicodeEncoding.GetBytes(StringToCrypt);
switch (CryptAction)
{
case "Encrypt":
byte[] encryptedData = Encrypt(_stringToCrypt, thisRSACryptoServiceProvider.ExportParameters(false));
_encryptedData = thisUnicodeEncoding.GetString(encryptedData);
break;
case "Decrypt":
byte[] decryptedData = Decrypt(_stringToCrypt, thisRSACryptoServiceProvider.ExportParameters(true));
_decryptedData = thisUnicodeEncoding.GetString(decryptedData);
break;
default:
break;
}
}
static private byte[] Encrypt(byte[] DataToEncrypt, RSAParameters keyInfo)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.ImportParameters(keyInfo);
return RSA.Encrypt(DataToEncrypt, false);
}
static private byte[] Decrypt(byte[] DataToDecrypt, RSAParameters keyInfo)
{
#region Temporary Assignment - Remove before build
byte[] tmpVal = null;
#endregion
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
try
{
RSA.ImportParameters(keyInfo);
#region Temporary Assignment - Remove before build
tmpVal = RSA.Decrypt(DataToDecrypt, false);
#endregion
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Exception Thrown");
}
#region Temporary Assignment - Remove before build
return tmpVal;
#endregion
}
}
}
Is there anything that I can change in this class that would allow me to check the encoding prior to passing the byte array to Encrypt / Decrypt?
It seems like I have a reference around here somewhere, but I am becoming frustrated, so I thought it would at least help if I stopped to do something other than reading and compiling...
BTW, I am calling this class to write to a password to an XML file using the Nini initialization framework.
http://nini.sourceforge.net/manual.php#ASimpleExample
Also, I used Notepad2 to change the file encoding (UTF-8) before I wrote to the XML file.
That was after the program halted after I compiled the first time. Using the debugger, I was able to see that the encoding was different between the XML data in memory (UTF-8) and the data on disk (ANSI).
That does not appear to be the case now, but the program still halts, referencing bad data returned from the Decrypt portion of RSAcrypt().
(also note that Encrypt and Decrypt were identical methods before my frustration set in, they do function the same, but I wanted to try to capture addition exception information related to the bad data claim. Of course, you will notice that I allowed my frustration to handicap my code ;-) )
Any suggestions, ideas or references would be great.
TIA,
E

Inside your constructor you generate a new RSA keypair each time when you do:
RSACryptoServiceProvider thisRSACryptoServiceProvider = new RSACryptoServiceProvider();
Since your constructor is where you encrypt and decrypt, you are encrypting with an RSA Key, and decrypting with a completely different one.
To make this work, you have several options based on how you plan to use your code.
One option is to export the RSA key, and use that for all encryption/decryption operations. This is the only option if you plan on decrypting/encrypting data between different runs of your executable.
Of course this completely glosses over how you will store your public/private key (I recommend DPAPI on windows), for use by your application.

Related

How to encrypt plain text using RSA asymmetric encryption

I am working on an application where I need to encrypt plain text using the RSA algorithm. I encrypt the plain text but it is not working as it gives Error Decoding Text. Basically, I am calling third-party API which gives me the error. When I encrypt my text using this link reference link it works perfectly fine so I think I am doing something wrong. Here is my code
public static string Encryption(string strText)
{
var publicKey = #"<RSAKeyValue><Modulus>MIIDSjCCAjKgAwIBAgIEWrJUKTANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJE
RTEPMA0GA1UECAwGQmF5ZXJuMQ8wDQYDVQQHDAZNdW5pY2gxDzANBgNVBAoMBkxl
eGNvbTEkMCIGA1UEAwwbQWdyb3BhcnRzX0RNU19CYXNrZXRfVXBsb2FkMCAXDTE4
MDMyMTEyNDYzM1oY################################################
A1UECAwG########################################################
################################################################
WaOa0parvIrMk9/#################################################
NCIeGu+epwg8oUCr6Wd0BNATNjt8Tk64pgQvhdX9/KRDSC8V4QCJBiE3LQPHUVdN
nWRixrcOpucMo6m9PPegjnicn/rBKdFZLfJqLHHm+TrHrNCsEQIDAQABMA0GCSqG
SIb3DQEBCwUAA4IBAQBGwlNnDh2UaZphkEf70MPhySFVnTnLSxUFuwuWaDu8l7YP
zBMeJxcNk3HNiXPeba03GQBj+JqGAwDALJLityGeGEzlESfv/BsgQOONt+lAJUjs
b7+vr2e5REE/dpJZ1kQRQC##########################################
np+GstsdWjIWbL6L6VoqU18qLO5b0k8OoEMsP3akUTcj0w8JwD5V5iLqDhnv1aXK
kntkd/QmVCY6zlzH/dnTh8RNO2CfRtB1GEzNnkJB</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var testData = Encoding.UTF8.GetBytes(strText);
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
rsa.FromXmlString(publicKey);
byte[] data = Encoding.UTF8.GetBytes(strText);
byte[] cipherText = rsa.Encrypt(data,true);
var base64Encrypted = Convert.ToBase64String(cipherText);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
}
}
Here is my public key. I am using an RSA certificate. I am passing the certificate key to the module tag here is my key. I think I might be using it wrong.
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIEWrJUKTANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJE
RTEPMA0GA1UECAwGQmF5ZXJuMQ8wDQYDVQQHDAZNdW5pY2gxDzANBgNVBAoMBkxl
eGNvbTEkMCIGA1UEAwwbQWdyb3BhcnRzX0RNU19CYXNrZXRfVXBsb2FkMCAXDTE4
MDMyMTEyNDYzM1oY################################################
A1UECAwG########################################################
################################################################
WaOa0parvIrMk9/#################################################
NCIeGu+epwg8oUCr6Wd0BNATNjt8Tk64pgQvhdX9/KRDSC8V4QCJBiE3LQPHUVdN
nWRixrcOpucMo6m9PPegjnicn/rBKdFZLfJqLHHm+TrHrNCsEQIDAQABMA0GCSqG
SIb3DQEBCwUAA4IBAQBGwlNnDh2UaZphkEf70MPhySFVnTnLSxUFuwuWaDu8l7YP
zBMeJxcNk3HNiXPeba03GQBj+JqGAwDALJLityGeGEzlESfv/BsgQOONt+lAJUjs
b7+vr2e5REE/dpJZ1kQRQC##########################################
np+GstsdWjIWbL6L6VoqU18qLO5b0k8OoEMsP3akUTcj0w8JwD5V5iLqDhnv1aXK
kntkd/QmVCY6zlzH/dnTh8RNO2CfRtB1GEzNnkJB
-----END CERTIFICATE-----
Any help would be highly appreciated. The encryption through this code is not working. But when I used the mentioned link above and pass this key it worked fine.
The answer to my question is here. I solved my problem and I am posting it because maybe someone in the future will have the same issue I am facing and what mistake I did to achieve my requirements.
Findings
I found during my research there is a difference between Public Key and Certificate. I miss understood the terminology I was passing a certificate instead of passing Public Key for encryption. So one of the community members #Topaco basically redirected me to the correct path which helps me to solve my problem. There are steps involved if you have a public key then you can achieve encryption but if you have a certificate then first you need to get the public key by using the method GetRSAPublicKey. When you got your public key in XML form then you pass it to encrypt method to get your result.
Here is the coding
Program.cs
var x509 = new X509Certificate2(File.ReadAllBytes(#"D:\xyz.cer"));
string xml = x509.GetRSAPublicKey().ToXmlString(false);
var result = EncryptUtil.Encryption("start01!", xml);
Utility Class
public static string Encryption(string strText, string publicKey)
{
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
rsa.FromXmlString(publicKey);
byte[] data = Encoding.UTF8.GetBytes(strText);
byte[] cipherText = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
var base64Encrypted = Convert.ToBase64String(cipherText);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
So you can achieve encryption using the above code you need to pass RSAEncryptionPadding.Pkcs1 for encryption.
#happycoding #keephelping

How to properly verify data with rsa?

I want to sign a message with a private key and verify it with a public key, but I can't get it to work..
Here is how I sign the data (edited, but still not working):
public static string SignData(string message, string privateKey) {
byte[] plainText = ASCIIEncoding.Unicode.GetBytes(message);
var rsaWrite = new RSACryptoServiceProvider();
rsaWrite.FromXmlString(privateKey);
byte[] signature = rsaWrite.SignData(plainText, new SHA1CryptoServiceProvider());
return Convert.ToBase64String(signature);
}
Here is how I test the data (edited, still not working):
public static bool VerifyData(string sign, string publicKey, string orig) {
byte[] signature = Convert.FromBase64String(sign);
byte[] original = ASCIIEncoding.Unicode.GetBytes(orig);
var rsaRead = new RSACryptoServiceProvider();
rsaRead.FromXmlString(publicKey);
if (rsaRead.VerifyData(original, new SHA1CryptoServiceProvider(), signature)) {
return true;
} else {
return false;
}
}
I store the keypair as an xml string inside my account class. This function is executed in the constructor of account.cs:
public void addKeys() {
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);
privateKey = provider.ToXmlString(true);
publicKey = provider.ToXmlString(false);
}
I test the overall thing with this:
string signedHash = Utility.SignData("test" ,account.privateKey);
if (Utility.VerifyData(signedHash, account.publicKey, "test")) {
Console.WriteLine("WORKING!");
} else {
Console.WriteLine("SIGNING NOT WORKING");
}
Why isn't the overall thing working? My guess is that it doesn't work because of some encoding stuff.
return ASCIIEncoding.Unicode.GetString(signature);
The signature is arbitrary binary data, it isn't necessarily legal Unicode/UCS-2. You need to use an arbitrary encoding (https://en.wikipedia.org/wiki/Binary-to-text_encoding#Encoding_standards) to encode all of the arbitrary data. The most popular transport for signatures is Base64, so you'd want
return Convert.ToBase64String(signature);
And, of course, use Convert.FromBase64String in the verify method.
If you're compiling with a target of .NET 4.6 or higher you can also make use of the newer sign/verify API:
rsaRead.VerifyData(original, new SHA1CryptoServiceProvider(), signature)
would be
rsaRead.VerifyData(original, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)
While it might not look simpler, it prevents the allocation and finalization of the SHA1CryptoServiceProvider that the other method did, and it sets up for The Future when you may want to switch from Pkcs1 signature padding to PSS signature padding. (But the real advantage is that method is on the RSA base class instead of the RSACryptoServiceProvider specific type).

Decryption using System.Net.Security.Cryptography with the key

I have the below code to decrypt an encrypted cipher using the old Microsoft.Web.Services2.Security.Cryptography. How will I achieve the same using System.Net.Security.Cryptography class?
public byte[] DecryptData(byte[] EncryptedData, System.Security.Cryptography.RSA rsaKey)
{
Microsoft.Web.Services2.Security.Cryptography.RSA15EncryptionFormatter eFormatter = new Microsoft.Web.Services2.Security.Cryptography.RSA15EncryptionFormatter(rsaKey);
return eFormatter.Decrypt(EncryptedData);
}
EDIT
The code I use is below. But this does not decrypt the cipher. I do not have access the encryption methods. So I do not know what is the real text to see.
public byte[] DecryptData(byte[] encryptedData, System.Security.Cryptography.RSA rsaKey)
{
try
{
var csp = new RSACryptoServiceProvider(rsaKey.KeySize);
return csp.Decrypt(encryptedData, false);
}
catch (Exception e)
{
//debugger; //No exception here.
}
}
You use the RSACryptoServiceProvider.decrypt method, where the second parameter is false.

RSA: Encrypt password in javascript but failed to decrypt that in C#

I want apply the RSA encryption to my project, but encountered some troubles:
First, I have download the JavaScripts library from
http://www.ohdave.com/rsa/ ,and add reference to my project;
Second, I have define the RSA object and code to initialize that:
internal RSACryptoServiceProvider Rsa
{
get
{
if (HttpContext.Cache["Rsa"] != null)
{
RSACryptoServiceProvider encryptKeys = (RSACryptoServiceProvider)HttpContext.Cache["Rsa"];
return encryptKeys;
}
else
{
return new RSACryptoServiceProvider(1024);
}
}
set
{
HttpContext.Cache.Remove("Rsa");
HttpContext.Cache.Insert("Rsa", value);
}
}
public ActionResult SignUp()
{
this.Rsa = Security.GetRsa();
RSAParameters param= this.Rsa.ExportParameters(true);
//this will bind to view
TempData["exponent"] = Util.BytesToHexString(param.Exponent);
TempData["key"] = Util.BytesToHexString(param.Modulus);
UserInfo user = new UserInfo();
user.Birthday = DateTime.Now.Date;
return View(user);
}
private RSACryptoServiceProvider GetRsa()
{
RSACryptoServiceProvider Rsa = new RSACryptoServiceProvider(1024);
return Rsa;
}
3.then, on JavaScript side , I have code, it encrypt the password user input and the bind it control:
var hash = document.getElementById("Pwd").value;
var exponent = document.getElementById("exponent").innerHTML;
var rsa_n = document.getElementById("key").innerHTML;
setMaxDigits(131);
var key = new RSAKeyPair(exponent, "", rsa_n);
hash = encryptedString(key, "111");
document.getElementById("Pwd").value = hash;
document.getElementById("Pwd2").value = hash;
document.getElementById("error").innerHTML = "";
document.getElementById("submit").click();
4.when user click submit, my C# code get the encrypted pwd string and try to decrypt it but failed with exception: bad data:
[HttpPost]
public ActionResult SignUp(UserInfo user)
{
user.UserId = user.UserId.ToLower(); //ignore case
user.UserGUID = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.IsEnabled = false;
user.Pwd = Convert.ToBase64String(Rsa.Decrypt(Util.HexStringToBytes(user.Pwd), false));//Exception:Rsa.Decrypt throw bad data exception
who do you know how to fix it? thank you in advance.
I had a very similar problem in that most of the JavaScript based RSA encryption solutions wasn't "compatible" with .NET's implementation.
Almost all the implementations I found online had one or both of the following items causing the incompatibility with .NET's implementation.
The byte order encoding in JavaScript is different to the byte order that .NET used. This is a biggie as for example a string is represented with a different order of bytes in JS than it is in .NET so you'll need to convert before encrypting and after decrypting. I believe it's enough to just reverse the byte order to match .NET, but don't quote me on that.
Padding was different: .NET uses OAEP padding by default for RSA so the JS implementation of RSA should support the same padding too. I believe OAEP padding is also called PKCS#1 v2.0 padding, but don't quote me on that either.
Aside: I found an amazing JS library, called JavaScript.NET (from jocys.com) that mirrors tons of the .NET BCL functionality, including the RSA implementation, such that I could even use similar classes, properties and methods. Have a look at this. I can confirm it works with .NET RSA implementation. Give it a go - here are some links for it:
Jocys JS.NET Code Project demo
Jocys JS.NET Download
Hth

TripleDES: Specified key is a known weak key for 'TripleDES' and cannot be used

I'm using the .NET 3.0 class System.Security.Cryptography.MACTripleDES class to generate a MAC value. Unfortunately, I am working with a hardware device that uses "1111111111111111" (as hex) as a single-length DES key. The System.Security.Cryptography library does some sanity checking on the key and returns a Exception if you try to use a cryptographically weak key.
For example:
byte[] key = new byte[24];
for (int i = 0; i < key.Length; i++)
key[i] = 0x11;
byte[] data = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] computedMac = null;
using (MACTripleDES mac = new MACTripleDES(key))
{
computedMac = mac.ComputeHash(data);
}
throws an exception
System.Security.Cryptography.CryptographicException : Specified key is a known weak key for 'TripleDES' and cannot be used.
I know this is not a secure key. In production, the device will be flashed with a new, secure key. In the mean time, is there any way to inhibit this Exception from being thrown? Perhaps an app.config or registry setting?
Edit: The key would actually be 101010... due to the algorithm forcing odd parity. I'm not sure if this is universal to the DES algorithm or just a requirement in the payment processing work I do.
Edit 2: Daniel's answer below has some very good information about hacking .NET. Unfortunately, I wasn't able to solve my problem using this technique, but there is still some interesting reading there.
I wouldn't really recommend it, but you should be able to modify the IL-code that checks for weak keys using Reflector and the Add-in ReflexIL
edit:
Sorry, it took a while for me to load all of it up in my Virtual Machine (running Ubuntu) and didn't want to mess with Mono.
Install the ReflexIL Add-in: View -> Add-ins -> Add
Open ReflexIL: Tools -> ReflexIL v0.9
Find the IsWeakKey() function. (You can use Search: F3)
Two functions will come up, doubleclick the one found in System.Security.Cryptography.TripleDES
ReflexIL should have come up too. In the Instructions tab, scroll all the way down to line 29 (offset 63).
Change ldc.i4.1 to ldc.i4.0, this means the function will always return false.
In your assemblies pane (left one), you can now scroll up and click on "Common Language Runtime Library", the ReflexIL pane will give you an option to save it.
Important notes:
BACK UP your original assembly first! (mscorlib.dll)
mscorlib.dll is a signed assembly and you will need the .NET SDK (sn.exe tool) for ReflexIL to make it skip verification. I just checked this myself, you should already have this with Visual C# installed. Just click "Register it for verification skipping (on this computer)" when asked to.
I don't think I have to tell you to only use this on your development machine :)
Good luck! If you need additional instructions, please feel free to use the commentbox.
edit2:
I'm confused!
I completely removed the IsWeakKey check from the set_Key function in the mscorlib assembly. I am absolutely certain that I modified the correct function, and that I did it correctly. Reflector's disassembler does no longer show the check. The funny thing is however, that Visual C# still throws the same exception.
This leads me to believe that mscorlib must somehow still be cached somewhere. However, renaming mscorlib.dll to mscorlib.dll_ leads MSVC# to crash, so it must still be dependent on the original dll.
This is quite interesting stuff, but I think I've reached the point where I have no clue what is going on, it just doesn't make any sense! See attached image. :(
edit3:
I notice in Olly, that unlike assemblies such as mscoree, mscorsec and mscorwks; mscorlib.dll isn't actually located in:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
But instead, in what appears to be a non-existent location:
C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\6d667f19d687361886990f3ca0f49816\mscorlib.ni.dll
I think I am missing something here :) Will investigate this some more.
edit4:
Even after having patched out EVERYTHING in IsWeakKey, and played around with both removing and generating new native images (x.ni.dll) of mscorlib.dll using "ngen.exe", I am getting the same exception. I must be noted that even after uninstalling the native mscorlib images, it is still using mscorlib.ni.dll... Meh.
I give up. I hope someone will be able to answer what the hell is going on because I sure don't know. :)
I found out what you need to do. Fortunately there is a method that available that creates the ICryptoTranforms that doesn't check for weak keys. You also need to watch out for the base class as it also does sanity checks. Via reflection simply call out the _NewEncryptor method (you need to do a little more reflection, but that's the idea).
Luckily the MACTripleDES has a field of type TripleDES, so derive from MACTripleDES and replace it via reflection in the constructors. I have done all the work for you.
I can't verify that the correct MAC is generated, but no exceptions are thrown. Furthermore, you might want to doc comment the code and do exception handling (reflection failures - e.g. if the fields/methods are not there) - but this is SO; so I didn't bother.
using System;
using System.Reflection;
using System.Security.Cryptography;
using System.IO;
namespace DesHack
{
class Program
{
static void Main(string[] args)
{
byte[] key = new byte[24];
for (int i = 0; i < key.Length; i++)
key[i] = 0x11;
byte[] data = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] computedMac = null;
using (MACTripleDES mac = new MACTripleDESHack(key))
{
computedMac = mac.ComputeHash(data);
}
}
}
class MACTripleDESHack : MACTripleDES
{
TripleDES _desHack = new DesHack();
static FieldInfo _cspField = typeof(MACTripleDES).GetField("des", BindingFlags.Instance | BindingFlags.NonPublic);
public MACTripleDESHack()
: base()
{
RewireDes();
}
public MACTripleDESHack(byte[] rgbKey)
: base(rgbKey)
{
RewireDes();
}
private void RewireDes()
{
_cspField.SetValue(this, _desHack);
}
}
class DesHack : TripleDES
{
TripleDESCryptoServiceProvider _backing = new TripleDESCryptoServiceProvider();
static MethodInfo _newEncryptor;
static object _encrypt;
static object _decrypt;
public override int BlockSize
{
get
{
return _backing.BlockSize;
}
set
{
_backing.BlockSize = value;
}
}
public override int FeedbackSize
{
get
{
return _backing.FeedbackSize;
}
set
{
_backing.FeedbackSize = value;
}
}
// For these two we ALSO need to avoid
// the base class - it also checks
// for weak keys.
private byte[] _iv;
public override byte[] IV
{
get
{
return _iv;
}
set
{
_iv = value;
}
}
private byte[] _key;
public override byte[] Key
{
get
{
return _key;
}
set
{
_key = value;
}
}
public override int KeySize
{
get
{
return _backing.KeySize;
}
set
{
_backing.KeySize = value;
}
}
public override KeySizes[] LegalBlockSizes
{
get
{
return _backing.LegalBlockSizes;
}
}
public override KeySizes[] LegalKeySizes
{
get
{
return _backing.LegalKeySizes;
}
}
public override CipherMode Mode
{
get
{
return _backing.Mode;
}
set
{
_backing.Mode = value;
}
}
public override PaddingMode Padding
{
get
{
return _backing.Padding;
}
set
{
_backing.Padding = value;
}
}
static DesHack()
{
_encrypt = typeof(object).Assembly.GetType("System.Security.Cryptography.CryptoAPITransformMode").GetField("Encrypt").GetValue(null);
_decrypt = typeof(object).Assembly.GetType("System.Security.Cryptography.CryptoAPITransformMode").GetField("Decrypt").GetValue(null);
_newEncryptor = typeof(TripleDESCryptoServiceProvider).GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
}
public DesHack()
{
}
public override ICryptoTransform CreateDecryptor()
{
return CreateDecryptor(_key, _iv);
}
public override ICryptoTransform CreateEncryptor()
{
return CreateEncryptor(_key, _iv);
}
public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV)
{
// return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Decrypt);
return (ICryptoTransform) _newEncryptor.Invoke(_backing,
new object[] { rgbKey, ModeValue, rgbIV, FeedbackSizeValue, _decrypt });
}
public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)
{
// return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Encrypt);
return (ICryptoTransform) _newEncryptor.Invoke(_backing,
new object[] { rgbKey, ModeValue, rgbIV, FeedbackSizeValue, _encrypt });
}
public override void GenerateIV()
{
_backing.GenerateIV();
}
public override void GenerateKey()
{
_backing.GenerateKey();
}
protected override void Dispose(bool disposing)
{
if (disposing)
((IDisposable) _backing).Dispose();
base.Dispose(disposing);
}
}
}
Instead of using MACTripleDES with the DES key repeated to fake a single DES CBC-MAC, you could just implement CBC-MAC yourself on top of DESCryptoServiceProvider.
<1111111111111111> is not a weak DES key.
This will calculate a DES CBC-MAC:
public static byte[] CalcDesMac(byte[] key, byte[] data){
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Key = key;
des.IV = new byte[8];
des.Padding = PaddingMode.Zeros;
MemoryStream ms = new MemoryStream();
using(CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write)){
cs.Write(data, 0, data.Length);
}
byte[] encryption = ms.ToArray();
byte[] mac = new byte[8];
Array.Copy(encryption, encryption.Length-8, mac, 0, 8);
PrintByteArray(encryption);
return mac;
}
Unfortunately, the behaviour can't be overridden.
There is a great suggestion using reflection in the MSDN forums
I'm not a security expert but wouldn't XORing your key with another value be enough to satisfy the sanity check? You could do this for your debug version (with proper IFDEF) so you can do proper checking and remove it for your release or production version where the key would be strong enough.
The reflection based solutions get you around the problem, but they are dirty and evil. Nobody has yet mentioned a very useful method: TripleDES.IsWeakKey
I have had this problem and solved it with a very simple utility that I use immediately before I set the Key on my CryptoServiceProvider:
private void MakeSecureKey(byte[] key)
{
while(TripleDES.IsWeakKey(key))
{
var sha = SHA256Managed.Create().ComputeHash(key);
Array.Copy(sha,key,key.Length);
}
}
If you call it anytime you make an encryptor or decryptor, it should prevent the crash and always give you a secure key.
Quite simple (After looking at the code from GitHub)
static bool TripleDES.IsWeakKey(Byte[] rgbKey)
Since it is static - it is easy to test your key against it
Size must be either 16 or 24 bytes (???)
Why can't they put it in the documentation
The code check for few simple repetitions
Just create random enuogh values
See code at: https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/TripleDES.cs
Dekel

Categories

Resources