I'm trying to create an RSA PKCS v1.5 key from a modulus and exponent, this is how far I got:
BigInteger mod = BigInteger.Parse(loginData["publickey_mod"], System.Globalization.NumberStyles.HexNumber);
BigInteger exp = BigInteger.Parse(loginData["publickey_exp"], System.Globalization.NumberStyles.HexNumber);
string timestamp = loginData["timestamp"];
string steamid = loginData["steamid"];
RSAParameters loginRSA = new RSAParameters();
loginRSA.Modulus = mod.ToByteArray();
loginRSA.Exponent = exp.ToByteArray();
However after searching the web for quite some times (>1 hour) I couldn't find anyway to create an RSA key... I need to create one to encode a password. Is there anyone that could help me a little further?
Once you have the RSAParameters of the public key information then just import it directly into an RSA instance:
using (var rsa = new RSACryptoServiceProvider())
{
// Import public key
rsa.ImportParameters(loginRSA);
// Encrypt some data
var cipherText = rsa.EncryptValue(someData);
}
Related
When porting a snippet of code from Java to C#, I have come across a specific function which I am struggling to find a solution to. Basically when decoding, an array of bytes from an EC PublicKey needs to be converted to a PublicKey object and everything I have found on the internet doesn't seem to help.
I am developing this on Xamarin.Android using Java.Security libraries and BouncyCastle on Mono 6.12.0.
This is the code I am using in Java:
static PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256r1");
KeyFactory kf = KeyFactory.getInstance("EC", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("secp256r1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), pubKey);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
return (ECPublicKey) kf.generatePublic(pubKeySpec);
}
This was the best solution I could come up with which didn't throw any errors in VS. Sadly, it throws an exception and tells me that the spec is wrong:
X9ECParameters curve = CustomNamedCurves.GetByName("secp256r1");
ECDomainParameters domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
ECPoint point = curve.Curve.DecodePoint(pubKey);
ECPublicKeyParameters pubKeySpec = new ECPublicKeyParameters(point, domain);
// Get the encoded representation of the public key
byte[] encodedKey = pubKeySpec.Q.GetEncoded();
// Create a KeyFactory object for EC keys
KeyFactory keyFactory = KeyFactory.GetInstance("EC");
// Generate a PublicKey object from the encoded key data
var pbKey = keyFactory.GeneratePublic(new X509EncodedKeySpec(encodedKey));
I have previously created a PrivateKey in a similar way where I generate a PrivateKey and then export the key in PKCS#8 format, then generating the object from this format. However I couldn't get this to work from an already set array of bytes.
Importing a raw public EC key (e.g. for secp256r1) is possible with pure Xamarin classes, BouncyCastle is not needed for this. The returned key can be used directly when generating the KeyAgreement:
using Java.Security.Spec;
using Java.Security;
using Java.Math;
using Java.Lang;
...
private IPublicKey GetPublicKeyFromBytes(byte[] rawXY) // assuming a valid raw key
{
int size = rawXY.Length / 2;
ECPoint q = new ECPoint(new BigInteger(1, rawXY[0..size]), new BigInteger(1, rawXY[size..]));
AlgorithmParameters algParams = AlgorithmParameters.GetInstance("EC");
algParams.Init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec ecParamSpec = (ECParameterSpec)algParams.GetParameterSpec(Class.FromType(typeof(ECParameterSpec)));
KeyFactory keyFactory = KeyFactory.GetInstance("EC");
return keyFactory.GeneratePublic(new ECPublicKeySpec(q, ecParamSpec));
}
In the above example rawXY is the concatenation of the x and y coordinates of the public key. For secp256r1, both coordinates are 32 bytes each, so the total raw key is 64 bytes.
However, the Java reference code does not import raw keys, but an uncompressed or compressed EC key. The uncompressed key corresponds to the concatenation of x and y coordinate (i.e. the raw key) plus an additional leading 0x04 byte, the compressed key consists of the x coordinate plus a leading 0x02 (for even y) or 0x03 (for odd y) byte.
For secp256r1 the uncompressed key is 65 bytes, the compressed key 33 bytes. A compressed key can be converted to an uncompressed key using BouncyCastle. An uncompressed key is converted to a raw key by removing the leading 0x04 byte.
To apply the above import in the case of an uncompressed or compressed key, it is necessary to convert it to a raw key, which can be done with BouncyCastle, e.g. as follows:
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.EC;
...
private byte[] ConvertToRaw(byte[] data) // assuming a valid uncompressed (leading 0x04) or compressed (leading 0x02 or 0x03) key
{
if (data[0] != 4)
{
X9ECParameters curve = CustomNamedCurves.GetByName("secp256r1");
Org.BouncyCastle.Math.EC.ECPoint point = curve.Curve.DecodePoint(data).Normalize();
data = point.GetEncoded(false);
}
return data[1..];
}
Test: Import of a compressed key:
using Java.Util;
using Hex = Org.BouncyCastle.Utilities.Encoders.Hex;
...
byte[] compressed = Hex.Decode("023291D3F8734A33BCE3871D236431F2CD09646CB574C64D07FD3168EA07D3DB78");
pubKey = GetPublicKeyFromBytes(ConvertToRaw(compressed));
Console.WriteLine(Base64.GetEncoder().EncodeToString(pubKey.GetEncoded())); // MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0xk0H/TFo6gfT23ish58blPNhYrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg==
As can be easily verified with an ASN.1 parser (e.g. https://lapo.it/asn1js/), the exported X.509/SPKI key MFkw... contains the raw key, i.e. the compressed key was imported correctly.
I need to create RSA key pair with custom public exponent size (specifically 64 bytes). Creating like this:
var rsa = new RSACng();
PrivateKey = rsa.ExportParameters(true);
or this
var rsa = RSA.Create();
PrivateKey = rsa.ExportParameters(true);
Always results in 3-bytes exponent: 1,0,1.
I've seen people are writing, that RSACng should support custom size exponent, but I can not find any good example.
I wanna ask question about RSA sign on C#.
I have one document for sign. I have plain hex values of modulus, private exponent and public exponent. I dont' t want to signing with random generated keys. When I try with sign with sign tool, i have got result perfectly. But i can' t do it with C#. I am new for this kind of problem. So i am really stucked.
I looked a lot of sites and arcticles but i couldn't find any solution. Maybe there is but i can' t get it, i don' t know.
If you could explain to me with details(step-step), i will really be grateful.
In .NET you need the full set of CRT parameters for the private key, so if you have D you'll also need P, Q, DP, DQ, and InverseQ.
RSAParameters rsaParams = new RSAParameters();
rsaParams.Modulus = ParseHex(modulus);
rsaParams.Exponent = ParseHex(exponent);
rsaParams.D = ParseHex(d, rsaParams.Modulus.Length);
rsaParams.P = ParseHex(p, (rsaParams.Modulus.Length + 1) / 2);
rsaParams.Q = ParseHex(q, rsaParams.P.Length);
rsaParams.DP = ParseHex(dp, rsaParams.P.Length);
rsaParams.DQ = ParseHex(dq, rsaParams.P.Length);
rsaParams.InverseQ = ParseHex(inverseQ, rsaParams.P.Length);
RSA rsa = RSA.Create();
rsa.ImportParameters(rsaParams);
Where ParseHex(string str, int len=-1) is a routine which parses your hex strings to a fixed length array (if the hex string is short, the array should be left-padded with 0-byte values).
public string Encrypt(string Code)
{
string result = string.Empty;
byte[] encryptResult = null;
var CodeInByte = Encoding.ASCII.GetBytes(Code);
try
{
using (MemoryStream memo = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = KeySize;
AES.BlockSize = BlockSize;
var key = new Rfc2898DeriveBytes(CodeInByte, salt, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var encrypt = new CryptoStream(memo, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
encrypt.Write(CodeInByte, 0, CodeInByte.Length);
encrypt.Close();
}
encryptResult = memo.ToArray();
}
}
result = Convert.ToBase64String(encryptResult);
return result;
}
catch (Exception err)
{
MsgCode = 99;
MsgDesc = err.Message;
return string.Empty;
}
}
It's just a simple AES encrypting method from string
The point I want to ask, when generating the key, at
var key = new Rfc2898DeriveBytes(CodeInByte, salt, 1000);
is the key generated from inputted string, or it's just a random generated byte array?
and, is the salt needs to be static or not
As the documentation on MSDN suggests:
Rfc2898DeriveBytes takes a password, a salt, and an iteration count, and then generates keys through calls to the GetBytes method.
In other words, it will derive bytes using the input parameters you give it. If you give it different parameters, the derived key will be different. If you give it the same parameters, it will generate the same bytes.
Symmetrical encryption algorithms (such as AES) require a fixed length key - 16 bytes in this case for AES128. However, you don't want to mandate that passwords are fixed length as this makes them much easier to attack. You also might want much longer keys than a feasible password - AES256 would require a 32byte key, for example. Finally, passwords tend to be alphanumeric and perhaps have some symbols, whereas an encryption key is made up of bytes that can range from 0x00-0xFF, if you made the encryption key a 32 character ASCII password, then you'd reduce the range quite considerably as the printable ASCII character range is much smaller than 0x00-0xFF.
For this reason, you want to derive the encryption key from a given password in such a way that you get a strong key of the exact length you require. That's where Rfc2898DeriveBytes comes in.
I have a little hard time understanding the RSACryptoServiceProvider class... I'm supposed to encrypt a message of length 256 bits, with a key , which is also 256 bits long. Shouldn't the output of also be 256 bits long?
Here's my code:
//key generation
byte[] bytes = new byte[32];
var rng = new RNGCryptoServiceProvider();
rng.GetBytes(bytes);
k2 = bytes;
//encryption function
static public byte[] Encryption(byte[] Data, RSAParameters RSAKey, bool DoOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.ImportParameters(RSAKey);
encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
}
return encryptedData;
}
And then finally calculating
ciphertext = Encryption(k2, RSA.ExportParameters(false), false);
produces a byte[128] ciphertext aka 1024 bits. Shouldn't I get ciphertext of size byte[32]?
It seems that you use the key, k2, as data for RSA encryption. That's OK if you want e.g. to wrap a 256 bit AES key using RSA. But your RSA key is the second parameter, not the first.
The data in k2 is then padded (according to the older PKCS#1 v1.5 scheme), after which modular exponentiation will be performed using the public exponent and modulus of the RSA key. The modulus of the RSA key determines the key size. This modulus exponentiation will always produce a result between zero and modulus - 1. However, the result is always left-padded to the key size in bytes (with a function called I2OSP).
So it seems your result is 1024 bits, which means that your RSA key pair is also 1024 bits.