RSA/ECB/PKCS1Padding C# Decyription Error String To PrivateKey - c#

I'm getting an error in Private Key conversion, I can't decrypt.
Error: System.InvalidCastException: Could not cast object of type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair' to type 'Org.BouncyCastle.Crypto.AsymmetricKeyParameter'.
When I convert it to AsymmetricCipherKeyPair type, the type does not match in the bottom line. I am waiting for your help.
static void Main()
{
var plainData = "plain_text";
RSA publicKeyEncryptor = getRSAPublic(#"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlYB5JrwA9fMxZxTRhG0NnKRwJizMZGJNq/xFfFxaEmKp3O6vZgsZMlFTi2kSC++yR/KriGKuGgbIYrgomn7BueoooAw5KLVO9CKKtNyQgg28vdOBbnQqljA+KID0PouAD8MqpDk9opi41zeEQPOSkAUsq5sHMptG7h9cgj0mNr2c4ffNolHAhPsrZVtGYtswhtznDkG463VOKLAmDLDeY9bASUsQXFOY+Em93GHFjStgZSTIEBof6HbUqIQf2rGjuPYCQsB/94BFma58epGz12zUPwKFMuxg89wbLOCjyAkocgS9zDnwKr7DVv08GmCUVVqI6ySzbWpKhiqWQvz4hwIDAQAB");
var plainBytes = Encoding.ASCII.GetBytes(plainData);
string encryptedPayload = System.Convert.ToBase64String(publicKeyEncryptor.Encrypt(plainBytes, RSAEncryptionPadding.Pkcs1));
RSA privateKeyDecyrpt = getRSAPrivate();
var y = privateKeyDecyrpt.Decrypt(Encoding.ASCII.GetBytes(encryptedPayload), RSAEncryptionPadding.Pkcs1);
Console.WriteLine(encryptedPayload);
}
public static RSA getRSAPublic(string publicKey)
{
string publicKeyPem = $"-----BEGIN PUBLIC KEY-----\r\n{ publicKey }\r\n-----END PUBLIC KEY-----\r\n";
var pemReader = new PemReader(new StringReader(publicKeyPem));
AsymmetricKeyParameter keyPairRaw = (AsymmetricKeyParameter)pemReader.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPairRaw);
RSA rsaObj = System.Security.Cryptography.RSA.Create();
rsaObj.ImportParameters(rsaParams);
return rsaObj;
}
public static RSA getRSAPrivate()
{
string privateKeyPem = #"-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAlYB5JrwA9fMxZxTRhG0NnKRwJizMZGJNq/xFfFxaEmKp3O6v
ZgsZMlFTi2kSC++yR/KriGKuGgbIYrgomn7BueoooAw5KLVO9CKKtNyQgg28vdOB
bnQqljA+KID0PouAD8MqpDk9opi41zeEQPOSkAUsq5sHMptG7h9cgj0mNr2c4ffN
olHAhPsrZVtGYtswhtznDkG463VOKLAmDLDeY9bASUsQXFOY+Em93GHFjStgZSTI
EBof6HbUqIQf2rGjuPYCQsB/94BFma58epGz12zUPwKFMuxg89wbLOCjyAkocgS9
zDnwKr7DVv08GmCUVVqI6ySzbWpKhiqWQvz4hwIDAQABAoIBAEJFR+8Gqbpcykpy
bQmxubX1Io2ZkCTzepDBbB/bZEYAHGIGIBQw2UN3z3vd4JUP9Mx14tm7PIfm987i
6YTKqZ97D/UaVgAYlt4brbbMivZLlp3i8t3+ep5G1lboCtzqw6K5Fd7kTNEVt+IX
BvYvwok68flD6GXjdQa7Oiu1ZYofxXOBg84RDWu6E6edDj/XaaX12WOYaQ4p6zDn
jyYNMiYYkKoUI1a884cN50WAAIGs0n3TxgJGXZa0n/Y3CpvuphAoHPQQs7ZyJPlP
k29gaFXdotzv1TDKk0I+eTIlonMl1EyW0wDPvfMuV7EYNNfmtmqrT3OWkOpUU8fO
9eDKhukCgYEA5nlVlTKAx5LsKI3KGRQF3jiWAaQAUKoON68Sx47fSJSv317QyZqM
qbQTLz5vSytbTyHhgIEc2dtdRbl7MYkvvzaizjPDEHEJSwlTmnzt81qGYtjNGC+j
tz6Bx5MGDE8Ax3ls60Lm0uH9TMQyJZWTepZGght5WXP5NpmzJ6zG7D0CgYEApg9S
zZyMN1rNtcz6Pqaqll33hchIQd+vh2LLKkHAoQP93oWtipxyT08yr4tQ5ke3nwjj
onIt6KR0U2aSIv9OUwXw2cSwghCIqTKmtont88WXYh9zRtnZ+U0An9r3x+fr7e8y
wpk7lFPP840pzQQjyEQ2s3woMlssqOiS6SyGMBMCgYB04KVBEypxixWN/1G05A2R
wxp3XIb4YTTykisw3khnU1fZLAkvo9ufl/1+oOfps+QLPkBQXamW5YLogAZ0eYCo
NHndnixW4yv2TJWEK8Sz+31ZFV702/vnSqCf5/RSO6JGhlJxAC10VjyROJHBs5fl
u92nz2z7qy9/u/Q5s4nxdQKBgAx6qFFVS2A5ja302nVs1vL32ssN8wgoRCubbAMf
79bp0uEvEIyTFzAIlpmEka7MgusLovepNvP9r9Q4qBDDOOKaVrA2zMDpdyun58ld
8ijYl3jDPkl7w5qtg7d/oBFAx4UY7aqcE1MhPUZjPFnwzrOVFLtGQEsQePm0iJ3H
P8pLAoGAUCC6KxGF20y0uBnSLS2q3iqAqZNO57ZLnJRAscsViNEhqn4G5OZbcANa
RsyFluCwjuKFg9yW9s6ZKzjPXsAEWTyXpayICSG4gJBRfdzUol5Sjo9JweqMhiVh
5G9uBYx3+kEpYNvJDcYK89HQ3fAcfeZ1o9sXYtwccOulSQ5euAE=
-----END RSA PRIVATE KEY-----";
var pemReader = new PemReader(new StringReader(privateKeyPem));
AsymmetricKeyParameter keyPairRaw = (AsymmetricKeyParameter)pemReader.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPairRaw);
RSA rsaObj = System.Security.Cryptography.RSA.Create();
rsaObj.ImportParameters(rsaParams);
return rsaObj;
}

The code essentially contains casting-related bugs that are most easily identified during debugging by determining the object types:
I'm getting an error in Private Key conversion, I can't decrypt. Error: System.InvalidCastException: Could not cast object of type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair' to type 'Org.BouncyCastle.Crypto.AsymmetricKeyParameter'.
You can't import the private key 1:1 like the public key, because both have different formats. The PemReader returns a different object type in the case of the private key, namely AsymmetricCipherKeyPair, which you cannot cast into an AsymmetricKeyParameter. This is what the error message says. So it must be:
AsymmetricCipherKeyPair keyPairRaw = (AsymmetricCipherKeyPair)pemReader.ReadObject();
When I convert it to AsymmetricCipherKeyPair type, the type does not match in the bottom line.
You need to modify this line as well. Here you have to pass a keyPairRaw.Private that must be cast to RsaPrivateCrtKeyParameters:
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPairRaw.Private);
With these changes the import of the private key works.
Another bug is in the decryption. The ciphertext is Base64 encoded during encryption, therefore it must be Base64 decoded during decryption and not ASCII encoded, i.e. correct is:
var decrypted = privateKeyDecyrpt.Decrypt(Convert.FromBase64String(encryptedPayload), RSAEncryptionPadding.Pkcs1);
Keep in mind that the options for importing keys in .NET are highly dependent on the version. There are versions where you can import PEM keys out-of-the-box (e.g. as of .NET 5), so that BouncyCastle is not needed.

Related

How to generate CSR with Public Key exponent and modulus only using CertificateRequest in C#

Trying to generate a CSR. so I only have public key exponent and modulus since the private key is in HSM.
So i generate an RSA object to pass CertificateRequest.
public static RSA GetRsaPublicKey(byte[] modulus, byte[] exponent)
{
RSA rsa = RSA.Create();
RSAParameters keyInfo = new RSAParameters
{
Modulus = modulus,
Exponent = exponent
};
rsa.ImportParameters(keyInfo);
return rsa;
}
But when calling CreateSigningRequest() or CreateSelfSigned() methods it throws exception:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException:
'Key does not exist.'
The tried to use this overload CertificateRequest(X500DistinguishedName, PublicKey, HashAlgorithmName).
But unable to construct PublicKey 2nd argument.
I then found this method but getting this error:
var gen = X509SignatureGenerator.CreateForRSA(rsa, RSASignaturePadding.Pkcs1);
var req = new CertificateRequest(new X500DistinguishedName(subject), gen.PublicKey, HashAlgorithmName.SHA256);
var bytes = req.CreateSigningRequest();//exception thrown
This method cannot be used since no signing key was provided via a
constructor, use an overload accepting an X509SignatureGenerator
instead.
Any help plz? on how to create a CSR without private key.
Certification Signing Requests contain the public key, but they are signed with the private key to prove that the private key holder authorized the options included in the request.
If you know that the CA you are sending the request to isn’t going to verify the signature, you could make a custom X509SignatureGenerator that produces the right algorithm identifier, but just writes a gibberish signature.
Thanks for the help #bartonjs.
Here is my solution that worked based on this: https://source.dot.net/#System.Security.Cryptography/System/Security/Cryptography/X509Certificates/X509SignatureGenerator.cs
public class CustomX509SignatureGenerator : X509SignatureGenerator
{
private readonly byte[] publicKey;
public CustomX509SignatureGenerator(byte[] publicKey)
{
this.publicKey = publicKey;
}
protected override PublicKey BuildPublicKey()
{
Oid oid = new Oid("1.2.840.113549.1.1.1");
// The OID is being passed to everything here because that's what X509Certificate2.PublicKey does.
return new PublicKey(
oid,
// Encode the DER-NULL even though it is OPTIONAL, because everyone else does.
//
// This is due to one version of the ASN.1 not including OPTIONAL, and that was
// the version that got predominately implemented for RSA. Now it's convention.
new AsnEncodedData(oid, stackalloc byte[] { 0x05, 0x00 }),
new AsnEncodedData(oid, this.publicKey));
}
// https://source.dot.net/#System.Security.Cryptography/Oids.cs,2f70bfb7d65ebf89,references
public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlgorithm)
{
var oid = "1.2.840.113549.1.1.11";
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
writer.PushSequence();
writer.WriteObjectIdentifier(oid);
writer.WriteNull();
writer.PopSequence();
return writer.Encode();
}
// Gibberish
public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm)
{
return new byte[2] { 0xAA, 0xBB };
}
}
// then call
var csrBytes = req.CreateSigningRequest(new CustomX509SignatureGenerator(rsa.ExportRSAPublicKey()));

'ASN1 corrupted data.' error when replacing public key but not private key

learning the mechanics of signing with RSA and I have a block of code that works below.
var privateRSAKey = File.ReadAllText("RSAPrivateKey.txt").Trim();
Regex privateRSAKeyRegex = new Regex(#"-----(BEGIN|END) RSA PRIVATE KEY-----[\W]*");
privateRSAKey = privateRSAKeyRegex.Replace(privateRSAKey, "");
//byte[602]
byte[] rsaPrivateKeyBytes = Convert.FromBase64String(privateRSAKey);
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(new ReadOnlySpan<byte>(rsaPrivateKeyBytes), out _);
But a similar block won't work for replacing the public key on another rsa object.
publicRSAKey = File.ReadAllText("RSAPublicKey.txt").Trim();
Regex publicRSAKeyRegex = new Regex(#"-----(BEGIN|END) PUBLIC KEY-----[\W]*");
publicRSAKey = publicRSAKeyRegex.Replace(publicRSAKey, "");
//byte[162]
byte[] rsaPublicKeyBytes = Convert.FromBase64String(publicRSAKey);
RSA recipientRSA = RSA.Create();
recipientRSA.ImportRSAPublicKey(new ReadOnlySpan<byte>(rsaPublicKeyBytes), out _);
I just want to replace the public rsa key from a string file with but i get the error
An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in System.Security.Cryptography.Algorithms.dll
ASN1 corrupted data.
I found this literally after posting
https://vcsjones.dev/key-formats-dotnet-3/
To summarize each PEM label and API pairing:
“BEGIN RSA PRIVATE KEY” => RSA.ImportRSAPrivateKey
“BEGIN PRIVATE KEY” => RSA.ImportPkcs8PrivateKey
“BEGIN ENCRYPTED PRIVATE KEY” => RSA.ImportEncryptedPkcs8PrivateKey
“BEGIN RSA PUBLIC KEY” => RSA.ImportRSAPublicKey
“BEGIN PUBLIC KEY” => RSA.ImportSubjectPublicKeyInfo
My issue was that my key was in the format -----BEGIN PUBLIC KEY-----
and I was using ImportRSAPublicKey .
I switched to .ImportSubjectPublicKeyInfo and all is well

How to convert RSA public key to string using BouncyCastle c#

I'm trying to save RsaKeyParameter Public Key into an SQL database. I get an error that Bouncy Castle can't convert RsaKeyParameters to bytes.
Using BouncyCastle c#.
I've generate an RSA key pair, extracted the private and public keys into variables. I then need to store the public key for verification at a later stage in the application.
I found a post on converting to byte then string as follows;
byte[] serializedPublicBytes =
publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublic = Convert.ToBase64String(serializedPublicBytes);
but it doesn't like ToAsn1Object. Just to add this is an example, I'm aware my variable names are different.
RsaKeyPairGenerator rsaKeyPairGen = new RsaKeyPairGenerator();
rsaKeyPairGen.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
AsymmetricCipherKeyPair keyPair = rsaKeyPairGen.GenerateKeyPair();
RsaKeyParameters PrivateKey = (RsaKeyParameters)keyPair.Private;
RsaKeyParameters PublicKey = (RsaKeyParameters)keyPair.Public;
The public key should to byte, then string, to save into the database.
The public key can be converted to the X.509/SubjectPublicKeyInfo-ASN.1/DER-format using BouncyCastle. This is a binary format from which a string can be generated using Base64-encoding:
byte[] publicKeyDer = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey).GetDerEncoded();
String publicKeyDerBase64 = Convert.ToBase64String(publicKeyDer);
Here, publicKey is the public key stored in the RsaKeyParameters-instance. The reverse process is:
byte[] publicKeyDerRestored = Convert.FromBase64String(publicKeyDerBase64);
RsaKeyParameters publicKeyRestored = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyDerRestored);
Detailed descriptions of the X.509/SubjectPublicKeyInfo- and ASN.1/DER-format can be found here and here, respectively.
Both, publicKeyDer (as hex-string) and publicKeyDerBase64, can be displayed in an ASN.1-Editor, e.g. https://lapo.it/asn1js/
Another approach is to create the PEM-format using the Org.BouncyCastle.OpenSsl.PEMWriter- and Org.BouncyCastle.OpenSsl.PEMReader-class
(not to be confused with Org.BouncyCastle.Utilities.IO.Pem.PEMWriter/PEMReader):
TextWriter textWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(textWriter);
pemWriter.WriteObject(publicKey);
pemWriter.Writer.Flush();
String publicKeyPEM = textWriter.ToString();
and the reverse is:
TextReader textReader = new StringReader(publicKeyPEM);
PemReader pemReader = new PemReader(textReader);
RsaKeyParameters publicKeyRestored = (RsaKeyParameters)pemReader.ReadObject();
The PEM-format is essentially a textual representation of the DER-format using an implicit Base64-encoding (e.g. explained here) and a header (-----BEGIN PUBLIC KEY-----) and footer (-----END PUBLIC KEY-----). Therefore, the Base64-encoded part is identical (if line breaks are ignored) for both, publicKeyDerBase64 and publicKeyPEM.

convert PEM encoded RSA public key to AsymmetricKeyParameter

I am trying o create a method that constructs an AsymmetricKeyParameter from a PEM encoded public key. Unfortunately, pemReader.ReadObject() return null.
Here's a working solution for a private key: convert PEM encoded RSA private key to AsymmetricKeyParameter
What is wrong with this method?
static AsymmetricKeyParameter ReadPublicKeyFromPemEncodedString(string pemEncodedKey)
{
AsymmetricKeyParameter result = null;
using (var stringReader = new StringReader(pemEncodedKey))
{
var pemReader = new PemReader(stringReader);
var pemObject = pemReader.ReadObject(); // null!
result = ((AsymmetricCipherKeyPair)pemObject).Public;
}
return result;
}
Here is the PEM-encoded public key I am testing with. I have tried without the comment and also removing SSH2.
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "rsa-key-20170608"
AAAAB3NzaC1yc2EAAAABJQAAAQEAk0AmagKx285Ufbri/olc+f3WagL1Ho+DrYdD
SbuU7cJAq+uD9xGvvP9m2JavSP4wO9i9pB/cmCFMPoIj3oGJt1/cnLb/U2juneOw
6Uo0N3F8TXdyXfZNAIPhq/jw0YfIypTFTTvFkKXfTArIwW/bQBW8/dujFR8i5CxP
jRKRDOBEy0PPOLJDD0iUr9GX/h/EO4jQ7B/GszjhPiPx+gJCilaMY+jrSczjxpsK
OXzpZEdT1NqMrzgvIZPHYhQzAiw9vQzov3vezDwKgKcRrUixZ2B8uiEQNn7Wa2Qz
WF3vL+6CGflFNYQcc0leDQBe86baYhCollouP4jfaH9KcMkYYw==
---- END SSH2 PUBLIC KEY ----
Bouncy castle just does not understand this format of public key (SSH2) (you can verify this by looking at source code of PemReader if you would like to). Unfortunately I don't know how to convert it to appropriate format in C#, but you can do that with many tools, for example with ssh-keygen (also available in gitbash for windows), or openssl. Your public key will look like this when converted to PEM:
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEAk0AmagKx285Ufbri/olc+f3WagL1Ho+DrYdDSbuU7cJAq+uD9xGv
vP9m2JavSP4wO9i9pB/cmCFMPoIj3oGJt1/cnLb/U2juneOw6Uo0N3F8TXdyXfZN
AIPhq/jw0YfIypTFTTvFkKXfTArIwW/bQBW8/dujFR8i5CxPjRKRDOBEy0PPOLJD
D0iUr9GX/h/EO4jQ7B/GszjhPiPx+gJCilaMY+jrSczjxpsKOXzpZEdT1NqMrzgv
IZPHYhQzAiw9vQzov3vezDwKgKcRrUixZ2B8uiEQNn7Wa2QzWF3vL+6CGflFNYQc
c0leDQBe86baYhCollouP4jfaH9KcMkYYwIBJQ==
-----END RSA PUBLIC KEY-----
And it will be correctly handled by your current code, with a little change:
var pemReader = new PemReader(stringReader);
var pemObject = pemReader.ReadObject(); // null!
// it's already AsymmetricKeyParameter
result = ((AsymmetricKeyParameter)pemObject);

System.IO.IOException: -----END RSA PRIVATE KEY not found

I am trying to create an online database application using PHP for the server and C# form application for the client.
On the server I encrypt a simple string using a public RSA key with the PHPSecLib. Then the C# application receives the string and tries to decrypt it using the corresponding private key.
The bytes are base64 encoded on the server and decoded to bytes again by C#. I created the key pair using the PHPSecLib.
This is the code I use on the client application:
public string rsa_decrypt(string encryptedText, string privateKey) {
byte[] bytesToDecrypt = Convert.FromBase64String(encryptedText);
Pkcs1Encoding decrypter = new Pkcs1Encoding(new RsaEngine());
//the error occurs on this line:
AsymmetricCipherKeyPair RSAParams = (AsymmetricCipherKeyPair)new PemReader(new StringReader(privateKey)).ReadObject();
decrypter.Init(false, RSAParams.Private);
byte[] decryptedBytes = decrypter.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length);
string decryptedString = Convert.ToBase64String(decryptedBytes);
return decryptedString;
}
But, I get the following error on the line specified above^.
An unhandled exception of type 'System.IO.IOException' occurred in
BouncyCastle.Crypto.dll
Additional information: -----END RSA PRIVATE KEY not found
I believe there's nothing wrong with the key pair combo as I get an error before I even try to decrypt anything.
The privateKey parameter is currently hardcoded into the script using this format:
string privateKey = "-----BEGIN RSA PRIVATE KEY-----XXXXXXXX-----END RSA PRIVATE KEY-----";
So it seems to me the footer actually is included in the string... I have debugged and googled everywhere but I can't seem to solve it. I'm pretty new to RSA&Bouncycastle so maybe I'm just using wrong methods.
Hope you can help, thanks!
- G4A
P.S. This is my first Stackoverflow question, I just created an account, so if you could also give me some feedback on the way I formulated this question; great!
You need to add a new line between the pre/post encapsulation boundary text and the Base64 data, so:
string privateKey = "-----BEGIN RSA PRIVATE KEY-----\r\nXXX\r\n-----END RSA PRIVATE KEY-----";
This is because the pem specification allows for the existence of other textual headers between the two.
If this doesn't work
"-----BEGIN RSA PRIVATE KEY-----\r\nXXXXXXXX\r\n-----END RSA PRIVATE KEY-----"
please try this
"-----BEGIN RSA PRIVATE KEY-----
XXXXXXXX
-----END RSA PRIVATE KEY-----"
We converted the BOX Private Key to Base64 Format and stored the same in Azure Vault.
Convert key to Base64 using Base64Encode method, store in Azure Key Vault.
Retrieve the encoded string in code, decoded back using Base64Decode Method.
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public static string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
I recommend use \x0A instead of \r\n and
.
Because only this option worked for me.
So :
"-----BEGIN RSA PRIVATE KEY-----\x0AXXXXXXXX\x0A-----END RSA PRIVATE KEY-----"

Categories