RSA C# ImportParameters "The parameter is incorrect" exception - c#

I have a server (python) and a client (c#), I need to communicate between them temporarily using assymetric rsa cryptography.
When I connect to the server as the client, I send him my public key and he send me his. I use at the server the rsa library and I get there the server's public key parameters {n,e} I send those and receive them with a space between them. I seperate them and convert the modulus into a BigInteger using this function:
public static BigInteger GetBigInteger(string number)
{
BigInteger bigNum = new BigInteger();
for (int i = number.Length; i > 0; i--)
{
bigNum *= 10;
bigNum += (int) number[number.Length-i];
}
return bigNum;
}
public static void Connect(IPAddress ipAddress, int port)
{
try
{
string[] message;
byte[] data = new byte[1024];
srvr.Receive(data); //Recieve the server's public key.
int length = int.Parse(Encoding.ASCII.GetString(data.Take(4).ToArray()));
message = Encoding.ASCII.GetString(data.Skip(4).Take(length).ToArray()).Split(' ') ;
RSACryptoServiceProvider RSAserver = new RSACryptoServiceProvider(1024);
RSAParameters par = new RSAParameters();
par.Modulus = GetBigInteger(message[0]).ToByteArray(); // Saves the server's public key.
par.Exponent = new byte[] { 1, 0, 1 }; // Saves the server's public key.
RSAserver.ImportParameters(par);
addresseeKey = RSAserver.ToXmlString(false);
...
}
...
}
An exception is thrown on the ImportParameters line that says: "The parameter is incorrect" .
What's wrong?

BigInteger.ToByteArray() exports the data in little-endian order. RSAParameters wants all of its data in big-endian order.
There's also an issue that the modulus value should have its most significant bit set, and therefore BigInteger.ToByteArray() needs to add in a padding byte to prevent the number from being reinterpreted as being negative, so you'll need to trim that off.
Assuming that your python export is representing the modulus as a base-10 positive integer, your code will work (though BigInteger.Parse(string) would be more efficient) once you align the data properly.
byte[] tmp = GetBigInteger(message[0]).ToByteArray();
// Array.Resize (to a smaller number) trims off the high index values,
// so it's slightly more compact code to do this before the resize.
if (tmp.Length > 0 && tmp[tmp.Length-1] == 0)
Array.Resize(ref tmp, tmp.Length - 1);
Array.Reverse(tmp);
par.Modulus = tmp;

Related

Translating Elliptic Curve parameters (BC to MS)

I'm trying to generate ECDSA self-signed certificate as described in generate certificate using ECDSA. Putting all pieces from bartonjs's answer together and using Net.Framework 4.7 (or Net.Core 2.0) following code seems to be working although there are some ambiguities (at least one) left:
I'm not sure how to properly convert private key ('D' parameter) from BC-BigInteger to MS-byte[]. Using BigInteger.ToByteArray() throws exception:
CryptographicException: The specified key parameters are not valid.
Q.X and Q.Y are required fields. Q.X, Q.Y must be the same length. If
D is specified it must be the same length as Q.X and Q.Y for named
curves or the same length as Order for explicit curves.
while validating ECParameters (method ECParameters.Validate()). Using BigInteger.ToByteArrayUnsigned() provides much better results (one failure on several hundred generated key-pairs), but still...
When using ToByteArray() converted 'D' is usually one byte longer ('D' has 33 bytes vs D.X and D.Y has 32 bytes). Using ToByteArrayUnsigned() the 'D' is sometimes one byte shorter.
So my question is whether is is ok to use ToByteArrayUnsigned().
private const string NCryptExportPolicyProperty = "Export Policy";
private const string SignatureAlgorithm = "Sha256WithECDSA";
private static readonly ECCurve MsCurve = ECCurve.NamedCurves.nistP256;
private static readonly DerObjectIdentifier BcCurve = SecObjectIdentifiers.SecP256r1; // must correspond with MsCurve
public static X509Certificate2 Create()
{
// 1. generate keys:
IAsymmetricCipherKeyPairGenerator bcKeyGen = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
bcKeyGen.Init(new ECKeyGenerationParameters(BcCurve, new SecureRandom()));
ECPrivateKeyParameters bcPrivKey;
ECPublicKeyParameters bcPublKey;
bool validated;
ECParameters msEcp;
do
{
AsymmetricCipherKeyPair bcKeyPair = bcKeyGen.GenerateKeyPair();
bcPrivKey = (ECPrivateKeyParameters)bcKeyPair.Private;
bcPublKey = (ECPublicKeyParameters)bcKeyPair.Public;
// 2. ensure generated bc-keys can be translated to cng (see exception below)
msEcp = new ECParameters();
msEcp.Curve = MsCurve;
msEcp.D = bcPrivKey.D.ToByteArrayUnsigned(); // or bcPrivKey.D.ToByteArray() ??
msEcp.Q.X = bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y = bcPublKey.Q.YCoord.GetEncoded();
try
{
msEcp.Validate();
validated = true;
}
catch (Exception e)
{
// Validate() occasionally throws CryptographicException:
// The specified key parameters are not valid. Q.X and Q.Y are required fields. Q.X, Q.Y must be the same length. If D is specified it must be the same length as Q.X and Q.Y for named curves or the same length as Order for explicit curves.
// e.g.: D = 31, Q.X = 32, Q.Y = 32.
validated = false;
Console.WriteLine("D = {0}, Q.X = {1}, Q.Y = {2}. {3}: {4}", msEcp.D.Length, msEcp.Q.X.Length, msEcp.Q.Y.Length, e.GetType().Name, e.Message);
}
} while (!validated);
// 3. create x509 certificate:
X509V3CertificateGenerator bcCertGen = new X509V3CertificateGenerator();
bcCertGen.SetPublicKey(bcPublKey);
// .. set subject, validity period etc
ISignatureFactory sigFac = new Asn1SignatureFactory(SignatureAlgorithm, bcPrivKey);
Org.BouncyCastle.X509.X509Certificate bcX509Cert = bcCertGen.Generate(sigFac);
byte[] x509CertEncoded = bcX509Cert.GetEncoded();
X509Certificate2 msNewCert;
// 4. use translated (and validated) parameters:
using (ECDsaCng msEcdsa = new ECDsaCng())
{
msEcdsa.ImportParameters(msEcp);
CngKey msPrivateKey = msEcdsa.Key;
// 5. make private key exportable:
byte[] bytes = BitConverter.GetBytes((int)(CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport));
CngProperty pty = new CngProperty(NCryptExportPolicyProperty, bytes, CngPropertyOptions.Persist);
msPrivateKey.SetProperty(pty);
// 6. tie keys together:
using (X509Certificate2 msPubCertOnly = new X509Certificate2(x509CertEncoded))
{
msNewCert = MateECDsaPrivateKey(msPubCertOnly, msPrivateKey); // method from bartonjs's answer
}
}
return msNewCert;
}
Thank you in advance
When you are getting too many bytes (33 in this case) the first byte should be 0x00, and you need to remove it. When you are getting too few (technically speaking D=1 is valid) you need to insert zeros to fill the array out.
The reason is that .NET's structure expects D to look like it does to the underlying Windows CNG import API, which means that D is a fixed-with unsigned big endian big integer. BouncyCastle is giving you the BER INTEGER encoding, which requires inserting a 0x00 byte when the high bit of the most significant byte (bytes[0], big endian) is set in a number that should be considered positive.
BER also has a rule that the minimum number of bytes be used, which is why sometimes BouncyCastle gives a number that's too small.
Q.X and Q.Y are okay because the ECPoint encoding rules specify a fixed size big endian integer whose size is determined by the curve; which is why BouncyCastle has the GetEncoded method instead of just ToByteArrayUnsigned.
private static byte[] FixSize(byte[] input, int expectedSize)
{
if (input.Length == expectedSize)
{
return input;
}
byte[] tmp;
if (input.Length < expectedSize)
{
tmp = new byte[expectedSize];
Buffer.BlockCopy(input, 0, tmp, expectedSize - input.Length, input.Length);
return tmp;
}
if (input.Length > expectedSize + 1 || input[0] != 0)
{
throw new InvalidOperationException();
}
tmp = new byte[expectedSize];
Buffer.BlockCopy(input, 1, tmp, 0, expectedSize);
return tmp;
}
...
msEcp = new ECParameters();
msEcp.Curve = MsCurve;
msEcp.Q.X = bcPublKey.Q.XCoord.GetEncoded();
msEcp.Q.Y = bcPublKey.Q.YCoord.GetEncoded();
msEcp.D = FixSize(bcPrivKey.D.ToByteArrayUnsigned(), msEcp.Q.X.Length);
The following code will help you, you can generate algorithm using bouncy castle library:
private static ECDsa GetEllipticCurveAlgorithm(string privateKey)
{
var keyParams = (ECPrivateKeyParameters)PrivateKeyFactory
.CreateKey(Convert.FromBase64String(privateKey));
var normalizedECPoint = keyParams.Parameters.G.Multiply(keyParams.D).Normalize();
return ECDsa.Create(new ECParameters
{
Curve = ECCurve.CreateFromValue(keyParams.PublicKeyParamSet.Id),
D = keyParams.D.ToByteArrayUnsigned(),
Q =
{
X = normalizedECPoint.XCoord.GetEncoded(),
Y = normalizedECPoint.YCoord.GetEncoded()
}
});
}
and generate the token in the following way:
var signatureAlgorithm = GetEllipticCurveAlgorithm(privateKey);
ECDsaSecurityKey eCDsaSecurityKey = new ECDsaSecurityKey(signatureAlgorithm)
{
KeyId = settings.Apple.KeyId
};
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateJwtSecurityToken(
issuer: iss,
audience: AUD,
subject: new ClaimsIdentity(new List<Claim> { new Claim("sub", sub) }),
expires: DateTime.UtcNow.AddMinutes(5),
issuedAt: DateTime.UtcNow,
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(eCDsaSecurityKey, SecurityAlgorithms.EcdsaSha256));

Decrypt a larger text using RSA private key

I have to encrypt and decrypt a large string using RSA public key and private key. I have managed to encrypt a larger text using the following sample code
public static string Encrypt(string publicKey, string data, RsaKeyLengths length)
{
// full array of bytes to encrypt
byte[] bytesToEncrypt;
// worker byte array
byte[] block;
// encrypted bytes
byte[] encryptedBytes;
// length of bytesToEncrypt
var dataLength = 0;
// number of bytes in key
var keySize = 0;
// maximum block length to encrypt
var maxLength = 0;
// how many blocks must we encrypt to encrypt entire message?
var iterations = 0;
// the encrypted data
var encryptedData = new StringBuilder();
// instantiate the crypto provider with the correct key length
var rsaCryptoServiceProvider = new RSACryptoServiceProvider((int)length);
// initialize the RSA object from the given public key
rsaCryptoServiceProvider.FromXmlString(publicKey);
// convert data to byte array
bytesToEncrypt = Encoding.Unicode.GetBytes(data);
// get length of byte array
dataLength = bytesToEncrypt.Length;
// convert length of key from bits to bytes
keySize = (int)length / 8;
// .NET RSACryptoServiceProvider uses SHA1 Hash function
// use this to work out the maximum length to encrypt per block
maxLength = ((keySize - 2) - (2 * SHA1.Create().ComputeHash(bytesToEncrypt).Length));
// how many blocks do we need to encrypt?
iterations = dataLength / maxLength;
// encrypt block by block
for (int index = 0; index <= iterations; index++)
{
// is there more than one full block of data left to encrypt?
if ((dataLength - maxLength * index) > maxLength)
{
block = new byte[maxLength];
}
else
{
block = new byte[dataLength - maxLength * index];
}
// copy the required number of bytes from the array of bytes to encrypt to our worker array
Buffer.BlockCopy(bytesToEncrypt, maxLength * index, block, 0, block.Length);
// encrypt the current worker array block of bytes
encryptedBytes = rsaCryptoServiceProvider.Encrypt(block, true);
// RSACryptoServiceProvider reverses the order of encrypted bytesToEncrypt after encryption and before decryption.
// Undo this reversal for compatibility with other implementations
Array.Reverse(encryptedBytes);
// convert to base 64 string
encryptedData.Append(Convert.ToBase64String(encryptedBytes));
}
return encryptedData.ToString();
}
Then I tried to decrypt the larger text using the following code
/// <summary>
/// Encrypt an arbitrary string of data under the supplied public key
/// </summary>
/// <param name="publicKey">The public key to encrypt under</param>
/// <param name="data">The data to encrypt</param>
/// <param name="length">The bit length or strength of the public key: 1024, 2048 or 4096 bits. This must match the
/// value actually used to create the publicKey</param>
/// <returns></returns>
public static string Decrypt(string privateKey, string data, RsaKeyLengths length)
{
// full array of bytes to encrypt
byte[] bytesToDecrypt;
// worker byte array
byte[] block;
// encrypted bytes
byte[] decryptedBytes;
// length of bytesToEncrypt
var dataLength = 0;
// number of bytes in key
var keySize = 0;
// maximum block length to encrypt
var maxLength = 0;
// how many blocks must we encrypt to encrypt entire message?
var iterations = 0;
// the encrypted data
var decryptedData = new StringBuilder();
// instantiate the crypto provider with the correct key length
var rsaCryptoServiceProvider = new RSACryptoServiceProvider((int)length);
// initialize the RSA object from the given public key
rsaCryptoServiceProvider.FromXmlString(privateKey);
// convert data to byte array
bytesToDecrypt = Encoding.Unicode.GetBytes(data);
// get length of byte array
dataLength = bytesToDecrypt.Length;
// convert length of key from bits to bytes
keySize = (int)length / 8;
// .NET RSACryptoServiceProvider uses SHA1 Hash function
// use this to work out the maximum length to encrypt per block
//maxLength = ((keySize - 2) - (2 * SHA1.Create().ComputeHash(bytesToDecrypt).Length));
maxLength = ((keySize / 8) % 3 != 0) ?
(((keySize / 8) / 3) * 4) + 4 : ((keySize / 8) / 3) * 4; ;
// how many blocks do we need to encrypt?
iterations = dataLength / maxLength;
// encrypt block by block
for (int index = 0; index <= iterations; index++)
{
// is there more than one full block of data left to encrypt?
if ((dataLength - maxLength * index) > maxLength)
{
block = new byte[maxLength];
}
else
{
block = new byte[dataLength - maxLength * index];
}
// copy the required number of bytes from the array of bytes to encrypt to our worker array
Buffer.BlockCopy(bytesToDecrypt, maxLength * index, block, 0, block.Length);
// encrypt the current worker array block of bytes
decryptedBytes = rsaCryptoServiceProvider.Decrypt(block, true);
// RSACryptoServiceProvider reverses the order of encrypted bytesToEncrypt after encryption and before decryption.
// Undo this reversal for compatibility with other implementations
Array.Reverse(decryptedBytes);
// convert to base 64 string
decryptedData.Append(Convert.ToBase64String(decryptedBytes));
}
return decryptedData.ToString();
}
Actually the encryption is going on smoothly. No problem with it. But when I am trying to decrypt it. I am getting the following exception
Unhandled Exception:
System.Security.Cryptography.CryptographicException: Error occurred
while decoding OAEP padding.
Can anybody help me out?
Use stream cipher instead and only encrypt that key for this cipher with RSA. This may help if you need RSA because of its public-private key logic and want to use the different keys for encryption and decryption. With stream cipher you would be able encrypt and decrypt gigabytes of data no problem.
RSA is normally not used for really big amounts of data.
Maybe a bit late, but I found http://tekaris.com/blog/2013/02/08/encrypting-large-data-with-asymetric-rsacryptoserviceprovider/ helpful. The trick here obviously is to split the data, encrypt and re-join the data.

How to decrypt an encrypted data in c# by using RSACryptoServiceProvider() in ubuntu?

I am encrypting my data by using RSACryptoServiceProvider() class in c#. I want to decrypt the data in ubuntu, that was encrypted in c#. Can you suggest me which mechanism I need to follow in order to decrypt. Following function is for encryption:
public static void Encrypt(String PublicKey, String plainText, out String cipherText)
{
try
{
int dwKeySize = 1024;
// TODO: Add Proper Exception Handlers
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
rsaCryptoServiceProvider.FromXmlString(PublicKey);
int keySize = dwKeySize / 8;
byte[] bytes = Encoding.UTF32.GetBytes(plainText);
// The hash function in use by the .NET RSACryptoServiceProvider here is SHA1
// int maxLength = ( keySize ) - 2 - ( 2 * SHA1.Create().ComputeHash( rawBytes ).Length );
int maxLength = keySize - 42;
int dataLength = bytes.Length;
int iterations = dataLength / maxLength;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i <= iterations; i++)
{
byte[] tempBytes = new byte[(dataLength - maxLength * i > maxLength) ? maxLength : dataLength - maxLength * i];
Buffer.BlockCopy(bytes, maxLength * i, tempBytes, 0, tempBytes.Length);
byte[] encryptedBytes = rsaCryptoServiceProvider.Encrypt(tempBytes, true);
// Be aware the RSACryptoServiceProvider reverses the order
// of encrypted bytes after encryption and before decryption.
// If you do not require compatibility with Microsoft Cryptographic API
// (CAPI) and/or other vendors.
// Comment out the next line and the corresponding one in the
// DecryptString function.
Array.Reverse(encryptedBytes);
// Why convert to base 64?
// Because it is the largest power-of-two base printable using only ASCII characters
stringBuilder.Append(Convert.ToBase64String(encryptedBytes));
}
cipherText = stringBuilder.ToString();
}
catch (Exception e)
{
cipherText = "ERROR_STRING";
Console.WriteLine("Exception in RSA Encrypt: " + e.Message);
//throw new Exception("Exception occured while RSA Encryption" + e.Message);
}
}
Don't use RSA like that. It's not meant to be used that way and it's way too slow.
The right way is to use a symmetric algorithm, e.g. AES, and encrypt the key you used with RSA. See my old blog entry for C# code doing just that.
You need the same mechanisms, but in reverse. Try first, ask later.

C# Encrypt string with public RSA-1280 hex key

I've been trying to encrypt a password with a public RSA key that is sent to me by the server.
var csp = new CspParameters(1, "Microsoft Strong Cryptographic Provider");
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1280, csp);
byte[] key = ByteUtils.HexToBytes(client.RSAKey);
RSA.ImportCspBlob(key);
byte[] encrypted = RSA.Encrypt(Encoding.ASCII.GetBytes(password), true);
The hex key is provided in such format:
string key = "30819D300D06092A864886F70D010101050003818B0030818702818100C7BD672D8C634D443840AD809790852770D3A2E99F456D6516329E0205D0645C23FD001D4D070CEE368A20526FEB2402358C915D7E86102B1659AA8651C449C344599F72BE904B8E338E7002E9978453C5BBCCA51AC165AA265069E0EAB1411D11A2FFDD35E5A8296A6A2AF238945874E8206979B0A16E2E4260A161CAB5C905020111";
As the string is 320-bytes long in hex format, I assume the key is 160 bytes (RSA 1280)
Using this method, the provider keeps saying "Bad Version of provider.\r\n".
I've tried several methods, convert it to Base64, simply import it as ASCII / Unicode. Nothing worked so far.
EDIT: My HexToBytes function (which works afaik, it returns me correct 160-b array):
public static byte[] HexToBytes(string pValue)
{
// FIRST. Use StringBuilder.
StringBuilder builder = new StringBuilder();
// SECOND... USE STRINGBUILDER!... and LINQ.
foreach (char c in pValue.Where(IsHexDigit).Select(Char.ToUpper))
{
builder.Append(c);
}
// THIRD. If you have an odd number of characters, something is very wrong.
string hexString = builder.ToString();
if (hexString.Length % 2 == 1)
{
//throw new InvalidOperationException("There is an odd number of hexadecimal digits in this string.");
// I will just add a zero to the end, who cares (0 padding)
Log.WriteLine(LogLevel.Debug, "Hexstring had an odd number of hexadecimal digits.");
hexString += '0';
}
byte[] bytes = new byte[hexString.Length / 2];
// FOURTH. Use the for-loop like a pro :D
for (int i = 0, j = 0; i < bytes.Length; i++, j += 2)
{
string byteString = String.Concat(hexString[j], hexString[j + 1]);
bytes[i] = HexToByte(byteString);
}
return bytes;
}
Your public key is not in the correct format. It is not a CSP blob. It is a DER encoded SubjectPublicKeyInfo structure. You can find source code to parse it or you can write your own. Here is one example of such code.

C# Split byte[] array

I am doing RSA encryption and I have to split my long string into small byte[] and encrypt them. I then combine the arrays and convert to string and write to a secure file.
Then encryption creates byte[128]
I use this the following to combine:
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
When I decrypt I take the string, convert it to a byte[] array and now need to split it to decode the chunks and then convert to string.
Any ideas?
Thanks
EDIT:
I think I have the split working now however the decryption fails. Is this because of RSA keys etc? At TimePointA it encrypts it, then at TimePointB it tries to decrypt and it fails. The public keys are different so not sure if that is the issue.
When you decrypt, you can create one array for your decrypt buffer and reuse it:
Also, normally RSA gets used to encrypt a symmetric key for something like AES, and the symmetric algorithm is used to encrypt the actual data. This is enormously faster for anything longer than 1 cipher block. To decrypt the data, you decrypt the symmetric key with RSA, followed by decrypting the data with that key.
byte[] buffer = new byte[BlockLength];
// ASSUMES SOURCE IS padded to BlockLength
for (int i = 0; i < source.Length; i += BlockLength)
{
Buffer.BlockCopy(source, i, buffer, 0, BlockLength);
// ... decode buffer and copy the result somewhere else
}
Edit 2: If you are storing the data as strings and not as raw bytes, use Convert.ToBase64String() and Convert.FromBase64String() as the safest conversion solution.
Edit 3: From his edit:
private static List<byte[]> splitByteArray(string longString)
{
byte[] source = Convert.FromBase64String(longString);
List<byte[]> result = new List<byte[]>();
for (int i = 0; i < source.Length; i += 128)
{
byte[] buffer = new byte[128];
Buffer.BlockCopy(source, i, buffer, 0, 128);
result.Add(buffer);
}
return result;
}
I'd say something like this would do it:
byte[] text = Encoding.UTF8.GetBytes(longString);
int len = 128;
for (int i = 0; i < text.Length; )
{
int j = 0;
byte[] chunk = new byte[len];
while (++j < chunk.Length && i < text.Length)
{
chunk[j] = text[i++];
}
Convert(chunk); //do something with the chunk
}
Why do you need to break the string into variable length chunks? Fixed-length chunks, or no chunks at all, would simplify this a lot.
why not use a framework instead of doing the byte-stuff yourself?
http://www.codinghorror.com/blog/archives/001275.html
"the public keys are different"?
You encrypt with a private key, and decrypt with the public key that corresponds to the private key.
Anything else will give you gibberish.

Categories

Resources