Export RSAParameters to human readable format - c#

I have my RSA public key defined in XML file
<RSAKeyValue>
<Modulus>+Rfd2cRvtV2Jez0gPuuEupPbA0wUN6GLYx/CasPe+X8dxG+KXtb/iRgB+mTW/ynxApOR/+GLS7rbcqq6cH76FnZH4l/Ualovd1CtY906EYGQ7ldxmt/1UfB+O4PiE3e4y2BGYshSdjQcJMCAyAwGL2vNmygUB/OPntZtnm1steX62TA1OG0VivsrCG+hDon4QrZN6XLNXHU0zpCDeVuuD5edVwRQCd2kuzNrLXuGjOaSXxfgzy1xgAPVDqKknr9doAJ4pGu3AILmjyWKldNLWzppqAbKFcmUjWUWbouMbaqDfs7JazxCgeY1DMSYkpSd0HOB6zl2u41xlpBSyLg1EGUOnp5KBPQSzOWqgJhbVy59LK1BhnkE4/eHZQjDsj95G9afmQnffk/td1qUf+MuX0Qo9L9Ls9Dlw3VQH52wnLchCBgdzaminFRMN0JbNe8IRf0ZAI87ES1ND0adMBo//QElV0J+YyPBjrGdYavNiI0jvBNq7x6ex405CrW5/J86R2LmdBSoD3knWFKQVszNN8jiA+Rl8at6qVBfoSgISzqLNoaad1B2J6gRJzBu3VHo1pkbFYz21I72orvhnMI9cL9pwtyayLPPC65nkvL2ichKJM2vtRY79z7IBf++Byq5y9L8vIDghEqwaPW3GT3574K7x4Rc4XETFMO4idRscsE=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Here is my method where I'm getting the RSA from XML file:
using (RSA rsa = RSA.Create())
{
var xml = await File.ReadAllTextAsync(keyConfig.File);
RSAExtensions.FromXmlString(rsa, xml);
var paremeters = rsa.ExportParameters(false);
var modulus = paremeters.Modulus;
}
At this moment it works.
But I have not idea how to convert modulus to e.g string? Modulus is a byte array but when I'm trying to convert this using Encoding.... it returns strange values. How is the modulus represented in this byte array? What format is it?

The value can't be converted directly to a string, but it can be represented by a Base64 string:
var myString = Convert.ToBase64String(modulus);
This will return a string that can later be decoded to get the original binary data.

Related

How to extract the text private and public key from ECDsa or ECDsaCng object?

When creating an ECDSA object, and trying to extract the private and public key objects it returns a byte[]. But when trying to convert it to a string, the output it gives doesn't look right.
// Creating the object with default parameters
ECDsa ecdSa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
// Export parameters
ECParameters ecParamters = ecdSa.ExportParameters(true);
// Private Key
byte[] privateKey = ecParamters.D;
// Public key params
ECPoint publicKey = ecParamters.Q;
// Coordinates
byte[] publicKeyX = publicKey.X; // What format? int, double, etc
byte[] publicKeyY = publicKey.Y; // What format? int, double, etc
Above ECParameters give access to the curve, D (private key) and Q (public key).
Any idea what sort of formatting is being used in these byte arrays? how can we convert it to a string format, eg: private and public keys to PKCS#8?
D is simply the raw private key, X and Y are the two coordinates of the raw public key. These are 32 bytes each for P-256.
Since .NET Core 3.0 the direct export of private ECDSA keys in PKCS#8 format (ExportPkcs8PrivateKey()) and SEC1 format (ExportECPrivateKey()) is supported and of public ECDSA keys in X.509 format (ExportSubjectPublicKeyInfo()). A detour via ExportParameters() is not necessary! The exported keys are DER encoded.
Example:
ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
byte[] privatePkcs8Der = ecdsa.ExportPkcs8PrivateKey();
byte[] privateSec1Der = ecdsa.ExportECPrivateKey();
byte[] publicX509Der = ecdsa.ExportSubjectPublicKeyInfo();
The conversion from DER to PEM encoding is trivial: Base64 encode, insert a newline after every 64 characters and add the format specific header and footer. Alternatively you can use BouncyCastle (e.g. for PKCS#8 via Org.BouncyCastle.Utilities.IO.Pem.PemWriter):
using Org.BouncyCastle.Utilities.IO.Pem;
using System.IO;
...
StringWriter stringWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(stringWriter);
pemWriter.WriteObject((PemObjectGenerator)new PemObject("PRIVATE KEY", privatePkcs8Der)); // SEC1: EC PRIVATE KEY, X.509: PUBLIC KEY
Console.WriteLine(stringWriter.ToString());
Since .NET 5 the PEM encoding is also directly supported, but only for the import: ImportFromPem().
Note similar support exists for ECDH and RSA keys.

Getting issue when convert c# code to PHP as below

I am running this code:
var timeStamp = DateTime.UtcNow;
var sharedSecret = "xx";
var saltedString = timeStamp.ToString("2021-01-07T16:42:33.619667Z") + sharedSecret;
//Encoding saltedString using Unicode little-endian byte order
byte[] encodedSaltedString = Encoding.Unicode.GetBytes(saltedString);
//Hashing Algorithm used is SHA512
HashAlgorithm hash = new SHA512Managed();
//Compute Hash of encodedSaltedString
byte[] hashedEncodedString = hash.ComputeHash(encodedSaltedString);
//Convert hashed array to base64-encoded string
string signature = Convert.ToBase64String(hashedEncodedString);
I am then getting this result in C#:
"gQhjrLnY6fo44EeaaWaUBE1PY/8oEIRsUcK3AMSCVUCYMM4vRfxvQEEggXaHTF0GQbw4w2HbWArX1k6NnkzJFg=="
I converted to this code as below, but I am getting an issue. Can I get some help on this?
$timestamp = "2021-01-07T16:42:33.619667Z";
$sharedSecret = 'xx';
$saltedString = $timestamp.$sharedSecret;
$utf=mb_convert_encoding($saltedString, "UTF-16LE");
$signature = base64_encode(hash('sha512', $utf));
IN PHP I am getting this result:
ODEwODYzYWNiOWQ4ZTlmYTM4ZTA0NzlhNjk2Njk0MDQ0ZDRmNjNmZjI4MTA4NDZjNTFjMmI3MDBjNDgyNTU0MDk4MzBjZTJmNDVmYzZmNDA0MTIwODE3Njg3NGM1ZDA2NDFiYzM4YzM2MWRiNTgwYWQ3ZDY0ZThkOWU0Y2M5MTY=
But both should be same. The c# one is correct, I want the same in the php code as well.
From the PHP docs for hash:
hash ( string $algo , string $data , bool $binary = false ) : string|false
binary
   When set to true, outputs raw binary data. false outputs lowercase hexits.
You're not passing a value for $binary, so it's returning a string of hexadecimal characters.
The C# HashAlgorithm.ComputeHash method on the other hand binary data.
Since you're base64-encoding the result, you're presumably expecting the hash function to return binary data. You therefore need to pass true as the value for $binary:
$signature = base64_encode(hash('sha512', $utf, true));

RSA Public key encryption using modulus and exponent

I want to generate a public key using RSA with given Modulus and Exponent values.
public static string RSAPublic(string toEncrypt) {
var crypt = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
var buffer = CryptographicBuffer.ConvertStringToBinary(toEncrypt, BinaryStringEncoding.Utf8);
string publikKey = modulus + exponent;
publikKey.Replace("\r\n", "");
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(publikKey);
string pk = System.Convert.ToBase64String(plainTextBytes);
IBuffer keyBuffer = CryptographicBuffer.DecodeFromBase64String(pk);
CryptographicKey key = crypt.ImportPublicKey(keyBuffer, CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo); // Throws exception here, have tried using all the 4 available BlobTypes
// var key = crypt.CreateKeyPair(512);
var sigBuffer = CryptographicEngine.Encrypt(key, buffer, null);
string signature = CryptographicBuffer.EncodeToBase64String(sigBuffer);
return signature;
}
Following is the Exception Message : "ASN1 bad tag value met. (Exception from HRESULT: 0x8009310B)"
StackTrace : " at Windows.Security.Cryptography.Core.AsymmetricKeyAlgorithmProvider.ImportPublicKey(IBuffer keyBlob, CryptographicPublicKeyBlobType BlobType)
at MyProject.General.Utility.RSAPublic(String toEncrypt)"
I am not able to figure out the right way to generate the CryptographicKey necessary to create encrypted string. Any help would be appreciated.
you should have a look of your public key format,
the key format must obey ans.1 rule.
look at this question
RSA Public Key format
if you still not sure about the format, just use the AsymmetricKeyAlgorithmProvider to create two or three or more public keys and convert them to hex string.
you can find the format.
I use the rsapkcs1 to create a sample public key
30818902818100ae037f0bcb6b4a5b661d7fc43178133b190f12f6c4e0e3ca694d4ec47458862b89691cb06767aa5054a92fc61ec8dc5c53983341c78ba3b95faf887b108093b41632a2ae324b0aaccab4172d83d7691476a6a97683d595355bd0bfa1fa5ea4d9cf2d5836ddb471de1df34ec27b6f0c4f903a13b6700cfb08ada8e43cf3b0cf7b0203010001
if you use the RsaPkcs1 to create key, the public key would start with
30818902818100
ae037f0bcb6b4a5b661d7fc43178133b190f12f6c4e0e3ca694d4ec47458862b89691cb06767aa5054a92fc61ec8dc5c53983341c78ba3b95faf887b108093b41632a2ae324b0aaccab4172d83d7691476a6a97683d595355bd0bfa1fa5ea4d9cf2d5836ddb471de1df34ec27b6f0c4f903a13b6700cfb08ada8e43cf3b0cf7b
end with 0203010001
just join the header and the tail to fill the format.

AES key converting to string and byte[] C#

I have an application that generates a AES Key (using Security.Cryptography). I take that AES Key, convert it to string and put it in a cookie like this:
string keyToSend = Encoding.UTF8.GetString(CurrentKey);
HttpCookie sessionKeyCookie = new HttpCookie("SessionKey", JsonConvert.SerializeObject(keyToSend));
keyToSend looks like this: "���K��Ui ����&��Ӂ*��()".
Then, I want to take back that key and use it to decrypt something, and I do this:
string keyString = JsonConvert.DeserializeObject<string>(context.Cookies["SessionKey"].Value);
byte[] ascii = Encoding.ASCII.GetBytes(cevaString);
byte[] utf8 = Encoding.UTF8.GetBytes(cevaString);
byte[] utf32 = Encoding.UTF32.GetBytes(cevaString);
Also, my keyToString looks like this: "���K��Ui ����&��Ӂ*��()".
And my browser cookie looks like this: "�\u0010��K��Ui �\u0010�\u000f�\u001f\u0005�\u0012\u0018&��Ӂ*��()\u001e"
The initial key should have 256bits, so 32 entries in that array, but all my variables (ascii, utf8, utf32) have different lengths. Why is that, how can I retrieve the cookie and convert it to a byte[32] array?
It sounds like CurrentKey is arbitrary binary data - not a UTF-8 encoded string. If you've got arbitrary data which you need to encode as a string (e.g. an image, or encrypted or compressed data) you're usually best off using Base64 or hex encoding. Base64 is pretty easy:
string keyToSend = Convert.ToBase64String(CurrentKey);
...
byte[] recoveredKey = Convert.FromBase64String(keyString);

Bouncy Castle, RSA: transforming keys into a String format

I'm using RSA (Bouncy Castle API) in my C# project. I generated the keypair with this method:
RsaKeyPairGenerator r = new RsaKeyPairGenerator();
r.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
AsymmetricCipherKeyPair keys = r.GenerateKeyPair();
AsymmetricKeyParameter private_key = keys.Private;
AsymmetricKeyParameter public_key = keys.Public;
Now I want to save them in a txt file but the problem is that I can't convert them to a string format. I read in another post that keys must be serialized using:
PrivateKeyInfo k = PrivateKeyInfoFactory.CreatePrivateKeyInfo(private_key);
byte[] serializedKey = k.ToAsn1Object().GetDerEncoded();
Is it the right way? If yes, what should I do after this? Just convert them from byte[] to String?
You could also use PemWriter to store them in PEM format:
TextWriter textWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(textWriter);
pemWriter.WriteObject(keys.Private);
pemWriter.Writer.Flush();
string privateKey = textWriter.ToString();
Now privateKey contain something like this:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDFhB3xI1AzSMsKvt7rZ7gp2o/vd49zON89iL1ENvKkph7oFXa2
ew/hjzbAV33lHnFFlA/vA5SDCbggRyU1/SmjfuPJFEzFbgcPLuO1Sw6z+bWXiIFp
QNCOTIw11c0fbwk+kB2y4E1OkLv5f9atlNlekb4wBn8bMbFYtu6LGWjkFQIDAQAB
AoGANPoMteLM3hSGMSmkzOsS4Fb5CDe/SB5W8kA805ERX8wWuhUXtDYpukwZWVme
MNgLdagS5f7F1KfEtROgDW0Lv4/T/FWAbpgh8O2CPKYDY4ZXl8tmRH9dtYn/824l
veLxdgNjHwo5OMvuTSDMjC3tbg2UA3kmV4VAm5QotlRinUECQQDo4zvI5e81zWnS
kNrUARX80t432MOZyr0RvAaVJfBNQpJl0H0xp5LKP74hvPVO9WdJvJ0M4Z4QrmGO
bm2Hsfz5AkEA2R469YXxgPLDg/LvUQqfrl8Ji9+rip7eQP7iS/dt66NMS31/HymT
+HscEZ3qGlnQuyyyRR2rGQdhdjU42HNy/QJBAKbPTF1DxSmGbovyUauU84jaCW17
B++6dd6kDRr7x7mvO2lOAr50RwIy0h8cV6EsycTZIqy9VhigryP0GOQfKxECQA8T
uVZpab7hnNAh45qGGVabhOcwrhHfPGHZEU/jK7/sRBUN7vD0CzF7IxTaGXKhAAyv
auW/zKzdRVhXE858HeUCQQCGaaAg8GwM0qIS0nHRTLldu4hIGjKn7Sk0Z46Plfwr
oqPCtuP4ehX85EIhqCcoFnG6Ttr6AxSgNMZvErVxDBiD
-----END RSA PRIVATE KEY-----
Well, I don't know about the RSA-specific side, but once you've got an opaque binary string (i.e. it could contain any arbitrary data) the best bet for text conversion is Convert.ToBase64String(byte[]) which you can reverse with Convert.FromBase64String(string).
Do not use Encoding.GetString(byte[]) and Encoding.GetBytes(string) for this - the binary data isn't text in a particular encoding, and shouldn't be treated as such. You're almost bound to lose data if you try this.
This might be what you are looking out for:
http://www.rahulsingla.com/blog/2011/04/serializing-deserializing-rsa-public-private-keys-generated-using-bounty-castle-library
Try the following
RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator();
rsaKeyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(), XXX));
AsymmetricCipherKeyPair keys = rsaKeyPairGenerator.GenerateKeyPair();
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keys.Private);
// Write out an RSA private key with it's asscociated information as described in PKCS8.
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
// Convert to Base64 ..
string serializedPrivateString = Convert.ToBase64String(serializedPrivateBytes);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keys.Public);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublicString = Convert.ToBase64String(serializedPublicBytes);
If you convert the bouncycastle certificate to a .net certificate. The function to do this is in the bouncycastle lib (i believe it's in a class called DotNetUtilities). The RSACryptoServiceProvider has a function:
ToXmlString(bool includePrivateKey).
Which gives you an x representation of a certificate with if you want the private key containing all the components serialized to base64 seperately, exponent, modulus, and d (private exponent).

Categories

Resources