I'm currently trying to implement a decrypt method on c# for a DecodeService I have. In this DecodeService I need to have a method that can receive an input string (the cipher data) and a private key (that is generated in another service).
The generated key that I'm getting looks something like this:
-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA1d22OTGqObdaC1UZUu93fMI+MnpSTho8GNgP1ANjDT4KtcmRpe7pBCVCtBNmUJU3kLimXRLtbZDnrrpdaHVadNPdhJrd3G6Ofx3KHkC+dSgtQhEXkgs2lmrG8pnM/1Mn69pAQIxfitSw19gxIfdgtkQTl2Lh4FJMDFyC1FwneSzmaOnAjGDc3kFyUg5EMRgbD7/HgU4ULUDLvlOBi6tjpRrbDBnf+G5fcFmo8Aw5f1Hw/KR6KVcirR0RKrJyOhMgFfRZsaS9Ngj4GlCt0Kzl8VCQAdGEQrXeFShxX7ffrQa6TUJ26pZVFw5WtskprQefU5LWfOkb9JErsi6kcVyu7QIDAQABAoIBAQCf0PSxPMnmI3voaAlqjoSN+GGeOwZyUuyB7EO3vWgxpyE9w4lRpH0Amg+EndKheUByp2m6t8AxVk2rppzSyI92KjSpOUl9xig6GbrGbL9msUVQz5D7k+GQ+e39sK0eR7fXadsF4WAsUNBuG6JrrX7Z9XlmcYA1r4og2qjxpIPi6iqqS2DCaN4+Y/PgsouBsnWtpmSnPUihwM9Zn76CQ7a2FJgVF2eif1eZ8I/fzf5bUYAgpSvCbR//0Z3T78XDmbBuKFahpqKyreB2fmh9tlMZRQjuy6jlfU9SOHBRnyAoqpJU72fmFcLZEBaYT+jTCTWkQVuxMn3voV/jTDh1s6ABAoGBAPPdqA43ZIUwWGIORjUuHrMKZvSRi7v9IZdWFygpNiUvKrWcsNdigfmjKgisZfuGR+lh58w6I8P1k/mwk11VNC38KU2N3Z5yvkzlIYxHhnBjixmhjfx772VB7mmmVknDGWi9QeKkGlZ4dMnFCebHZcPF+nuQ4YWCXbhoXSJhbyQBAoGBAOCB66Z89I+YEeMWXWPZTXwig8UDVz+9aEQAyFBMsMRXqlcrRTZoiQQzqRPGP36AcmvKFtj8/E3D5Q/hAaoFSxD2nBI0FIto7iE6GofKdJTmCcgcQ7MCqGEt7nDtf/piA6z1ZEI9xwrzmcWbE5I15uVSB8z6XKEb7GILmlnq0FrtAoGAMZn6Hrohlf2ZXIhuu05gLCaHfmNEOr5n4LJAD4kiXDnVxA7U4VpF+ZK4b/8w1xIe+ldRoHaK48bFjFnf/3HfJSNO+1lJpHrOPjG0uyWpMJ7uog38JUjIDp50ccTFdEdcmbM0/XZHbSMfDhMnymrwRfu00QFM/azghyujFeUfiAECgYBVlHDVuBAppJg87kuPQkhtJ2L/eLSLkZHrSjkRkwpW7ev66n2prdN9DIxJA2s0t5XmTNTExjsPEbuBgA34V6gzwS3hKoUiV09ezx1J4fzqvX3BEDUmSb8RvEaGOaJtOWuZRLmBHrd/DzjwwJqHQ5DOnaRnmJAywcgr36xo1j+ySQKBgQDEwVzruJYXu6MSoiVd0NKeZ2+A1zIdSBPHTiL2c2ofEYFc+OFHxu4bMWbINzN4nn7MM3zSB6WollBrgj2HoSHHoMQGb4QZEKj6Orv3Tg9AbzgbWDiPZ12mboEpaJLaqQFbJbv1aIJoCFL890NMBLpad9xvyrwBTSK4dHOJyTrqGg==-----END RSA PRIVATE KEY-----
I've tried implementing this decrypt method the following way:
public string RSADecryptString(string inputString, string privateKey)
{
BCEngine bcEngine = new BCEngine(new AesEngine(), _encoding);
bcEngine.SetPadding(_padding);
return bcEngine.Decrypt(inputString, privateKey);
}
While looking for a BouncyCastle decrypt implementation I found the one above and also the privateKey I'm receiving is generated by BouncyCastle.
But I'm getting the error that my private key is not 128/192/256 bits. And I don't know how to validate it. Or maybe it is because the private key I'm receiving is in the wrong format for my decrypt method?
Thanks for your help.
Related
I have a .crt certificate and a .key private key file on a Linux machine. The private key is in encrypted PKCS#8 format (BEGIN ENCRYPTED PRIVATE KEY...). I would like to import these into an X509Certificate2 object for further use. Since we're on Linux, we're using .NET Core 2.2 (we cannot migrate to 3.0 yet).
I have explored a few possible solutions, detailed below:
Use openssl to convert the files to a .pfx and import that using X509Certificate2
I do not want to use this option since I don't want to execute shell code from within C#. I would like the solution to be completely programmatically achieved in C#.
Use the C# BouncyCastle libraries to do either:
A conversion of both the certificate and the key to .pfx (as above), or
Importing the certificate and private key separately and using X509Certificate2.CopyWithPrivateKey() to combine them.
However, I cannot find an API for the C# version of BouncyCastle, so I'm not sure what methods I could possibly use to do this.
Some other programmatic method in C# that I'm missing here
Essentially, the end goal is to obtain an X509Certificate2 object from the .crt and .key files. Any help/insight into what approach to use, or even a pointer to helpful BouncyCastle documentation, would be much appreciated. Thanks!
This is possible, though not as friendly as it could be, in .NET Core 3.0:
private static byte[] UnPem(string pem)
{
// This is a shortcut that assumes valid PEM
// -----BEGIN words-----\nbase64\n-----END words-----
const string Dashes = "-----";
int index0 = pem.IndexOf(Dashes);
int index1 = pem.IndexOf('\n', index0 + Dashes.Length);
int index2 = pem.IndexOf(Dashes, index1 + 1);
return Convert.FromBase64String(pem.Substring(index1, index2 - index1));
}
...
string keyPem = File.ReadAllText("private.key");
byte[] keyDer = UnPem(keyPem);
X509Certificate2 certWithKey;
using (X509Certificate2 certOnly = new X509Certificate2("certificate.cer"))
using (RSA rsa = RSA.Create())
{
// For "BEGIN PRIVATE KEY"
rsa.ImportPkcs8PrivateKey(keyDer, out _);
certWithKey = certOnly.CopyWithPrivateKey(rsa);
}
using (certWithKey)
{
Console.WriteLine(certWithKey.HasPrivateKey);
}
RSA private keys can be in three different formats, and you need to call the correct import for each one:
"BEGIN PRIVATE KEY": ImportPkcs8PrivateKey
"BEGIN ENCRYPTED PRIVATE KEY": ImportEncryptedPkcs8PrivateKey
"BEGIN RSA PRIVATE KEY": ImportRSAPrivateKey
In .NET 5 this got as easy as can be:
var certificate =
X509Certificate2.CreateFromPemFile(
crtFile,
Path.ChangeExtension(crtFile, "key"));
I have created a self-signed-certificate (pfx) for testing purposes.
I'm able to export ist public key via :
X509Certificate2 cer = new X509Certificate2();
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificateCollection = store.Certificates.Find(...);
var cert = certificateCollection[0] ;
Console.WriteLine(Convert.ToBase64String(cert.Export( X509ContentType.Cert ), Base64FormattingOptions.InsertLineBreaks));
Result :
MIIDFTCCAf2gAw...........eFUpBB9C0/UNRmD7EAg==
This is consistent with openssl command :
$ openssl pkcs12 -in domain.name.pfx -clcerts -nokeys -out domain.name.crt
Result :
-----BEGIN CERTIFICATE-----
MIIDFTCCAf2gAw........9C0/UNRmD7EAg==
-----END CERTIFICATE-----
However , when I export the private key via C# :
Convert.ToBase64String(cert.Export( X509ContentType.Pfx ), Base64FormattingOptions.InsertLineBreaks)
I get :
MIIDFTCCAf2gAw............OVeFUpBB9C0/UNRmD7EAg==
While with openssl command :
$ openssl pkcs12 -in domain.name.pfx -nocerts -nodes -out domain.name.key
I get :
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w.........6HNjF2h7uuFdvbH2VAVg=
-----END PRIVATE KEY-----
Question:
Why do I get different results in the private keys ? And how can I fix my C# code to yield the same result as openssl's result ?
(NB - I'm not talking about -----begin/end keys----- boundries , but the actual value.
Additional info , the PFX file
The X509ContentType.Cert representation is the same because the certificate is a static structure. If you export it in a loop you'll always get the same answer.
The X509ContentType.Pfx representation is different because there are random salts in the PFX structure. One for each certificate, one for each private key, and one final one. If you export the same public+private pair as a PFX in a loop it will be different every time (288 bits of random data).
The end of the base64 data is different because it contains the last salt and the MAC over the rest of the data (including the encrypted cert salt and the encrypted key salt). It also contains an integer work factor, which Windows chooses as 2000 and OpenSSL chooses as 2048; further differentiating the OpenSSL and Windows exports.
So what you are seeing is by design. If you need a stable export you'll have to do something else. If you were just concerned that they're different, it's almost certainly fine.
Why do I get different results in the private keys ?
You are trying to do two separate things. With openssl you are using pfx file that contains private key and certificate(s). In c# you are using some representation of certificate with private key and you are doing export to a pfx file!
And how can I fix my C# code to yield the same result as openssl's result ?
C# has no friendly function that could export only private key (in openssl-like way) from X509Certificate2. As #darksquirell42 mentioned in comments you could use X509Certificate2.PrivateKey property, then cast it to RSACryptoServiceProvider and do ToXmlString(true). But this will give you private key parameters in a xml structure. Then you would have to take each parameter and construct an ASN.1 structure PKCS#1 or (IMHO better) PKCS#8. But C# has by default no friendly methods to work with ASN.1 objects.
OR you could use some crypto library to do it for you like BouncyCastle. This method from BouncyCastle could be helpful.
I have used PuttyGen to create a public/private key pair as a .ppk file, but I'm having trouble actually using it in my code. Opening the randomly generated ppk file in notepad shows this:
PuTTY-User-Key-File-2: ssh-rsa
Encryption: aes256-cbc
Comment: rsa-key-20151217
Public-Lines: 6
AAAAB3NzaC1yc2EAAAABJQAAAQEAl3m8PRlx/SL7EJrs+hDQbP9mp27XXRY4pztg
v8mxAthI2tMEhF0eyXqFV0/W/M13pPs1hHh3H7wIfAy/XbxF7KPeOeMkThMmF2p1
cwJOcpFuh6TCPT09ScPLCR5bsmQyzvsjPMWahcoDrLhf9MGfc9luQs7k1eMTM1iX
hq6F/ku7mNQ4mgKoOOlXKPhE6dFz9Qhe5k0TE3zIfkXUCCkm+74VqyG5l5vG6/fb
ZmVD0nGM6ErPHB/zQ0WbTm65BMmLIIfNZoVrRwcrQmjj4qJnJg7s+Ar0wfB0Y1kl
91fcxKJ4Mx2uBw7T0T+61DrXKamnqQmTz8g/QsgwwBGLqz9mXQ==
Private-Lines: 14
uvM2bpGRiAQey2PSgAdoQHWn7nv925WBHXx5uSfrUZpx9+HwMjCAy7Y7vDwZUUwT
kYb5PH8acor9aeu7xAaTdUFuYWliHA023pbac9CxdRUE7xMCH8u8rTpo6ZKRKKmS
ExNfEXnk9Veqn2Kr2vbXOd/zc2hiJNrmlMdik1v6RMmOhGfNgHKytlvJbZCF9WDM
gkXpuKBO04cFA241PvJNVC0VdHg+DiNhnqdlUBxW/trR5OieIBgqXLTqeAjGsdt9
066F0DhUCguOrSl5WUlrM3I4ylTqn1Xb9Vr1/7iTnDUaStwGWxjMj9YqgjHjuF/9
Lq0lDdIAriXb9IcvHoU7JWPpOtr5JtGRK0MDAN3YEX4hutPcmFv3F6UHOCFOSBNQ
UTu+Hg+dGzInvN63d0YaZyoEO8n8OpVI8gjrsKsHDgk7crnIyIDi8KKF4skgE4jf
kpVHwW80WBw9zizJlnBFxCnrkigEvI6X+hKTbTM4il5wt7Ixcympgl8EvZc8TaI2
Ksqeg+ONeA2H4Wgi+bzkys111/oTDsC41JfjwWS1lanuKpyYm9SDWTSQf48KV8kH
7fIHQ5O/4RV+f7k19IIWmvCcZTqq9xzjJXtYrjYKbCsptPBq+AQjvXcwkV1DpoGP
q5FKpA0QR4VYlCYqU6h5EdE+EuJpL2r0fEQ1I3pYsH+AUjCvZT1TzB5qvh+0krAw
GI786w9LbuhNUWEGtO8AIS0876bTQq4xLlTlgsZSHG0ElUIHSLlwUQL9RLiN97OZ
sokrwZ/7tFMA4ZK9eOPHXIkgWiF4vnrf1CLN/ePuLKpXsGjYKXnLyQy4cM9tGsEA
cfTBsYqriIvlehgnREdzUiGFxHoJ95WMpK/ESCSwlAAW3EMTs9bZRxyi5hwpmjc8
Private-MAC: 5403e23591ff38245d34db92ef130c86e9789b98
And this is what's in the public key file:
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "rsa-key-20151217"
AAAAB3NzaC1yc2EAAAABJQAAAQEAl3m8PRlx/SL7EJrs+hDQbP9mp27XXRY4pztg
v8mxAthI2tMEhF0eyXqFV0/W/M13pPs1hHh3H7wIfAy/XbxF7KPeOeMkThMmF2p1
cwJOcpFuh6TCPT09ScPLCR5bsmQyzvsjPMWahcoDrLhf9MGfc9luQs7k1eMTM1iX
hq6F/ku7mNQ4mgKoOOlXKPhE6dFz9Qhe5k0TE3zIfkXUCCkm+74VqyG5l5vG6/fb
ZmVD0nGM6ErPHB/zQ0WbTm65BMmLIIfNZoVrRwcrQmjj4qJnJg7s+Ar0wfB0Y1kl
91fcxKJ4Mx2uBw7T0T+61DrXKamnqQmTz8g/QsgwwBGLqz9mXQ==
---- END SSH2 PUBLIC KEY ----
Obviously, I'm not using this key anymore, it's just for illustrative purposes. But how do I read this file into C# to use it to sign and verify a string?
You're making an RSA key in Putty and trying to use that, why not simply just generate the key pair with C#'s RSA encryption?
You can generate a public/private key-pair and save it using this:
//Generate a public/private key pair.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Save the public key information to an RSAParameters structure.
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
You can see how to use this key pair to encrypt data, example documentation here.
I am trying to implement a licensing solution with DSA Algorithms for my application. Now here is what I have done:
Generated a hardware key, taken its hash.
Generated public and private keys. And encrypted my hash function with private key.
I forward this encrypted value back to client along with the public key.
At client's system, I use the DSASignatureDeformatter's VerifySignature function to validate my encrypted key, and my hardware key. If equal I validate the client.
Now my problem is that how to send the public key over the network. I tried to store and forward various DSAParameters values e.g., J, G, P in a file but since the sizes of keys change, that is not viable. Please see if anyone can guide.
Updated:
When I try to do this at the client's machine
DSAParameters KeyInfo;
using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
{
// Import the key information.
KeyInfo = DSA.ExportParameters(false);
}
The key size it generates for its various members is different from the public key parameters I have sent it back from server.
Okay. A bit late. But maybe other ones will have the same question.
You should just export your key like this:
string publicKey = DSA.ToXmlString(false);
so you can import it like this:
using (DSACryptoServiceProvider dsa = new DSACryptoServiceProvider())
{
dsa.FromXmlString(publicKey);
return dsa.VerifySignature()
}
I am faced with a problem on converting PGP Public keys to RSA Public Key format. Following public key in PGP format that need to be converted to XML format.
Please help me, if any of you had encountered such scenarios.
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.1 (MingW32)
mQELBE2kxxwBCADjWSW/F/h9VkV9JizFwKXPzg1YlXyEmN/dGKTVBidOI3ZcrhQd
v3qhcWtNmBXz1BpO+YLy41AfJzysKNHrs23sMh8nKWlMSmKZZqVpimXCcCrKQ8Hi
uNNxcRN/hBdy5R4MN/N62hbFTgoZpGmrQjOXDSPozDeckQE7DjkGYvQAT63dT3jC
F99im/KC2GfHDxVh70+881ot+vUxKC6GRZAz6wth90M/5FzbKQt0GmFrq/XapqxK
ZQI5LLMzahdGzJTGEbfs8SlH6OAqZsgjgEKYaW3EooQVbrvGrdqna6PBkLbroCOl
EH/h6W9h42dFLb2OSV9OES7oidQATqzNC+oXAAYptE9CdWNrIENvbnN1bHRhbnRz
IC0gTXkgTWVkaWNhcmUgQWR2b2NhdGUgKE1NQSkgPHByb2RzdXBwb3J0QGJ1Y2tj
b25zdWx0YW50cy5jb20+iQE0BBMBAgAeBQJNpMccAhsDBgsJCAcDAgMVAgMDFgIB
Ah4BAheAAAoJEJrpFtACLTdGtKEH/3G4pB4UeiEsMwDYDO1YF4jkz2lqbItmCGek
WuVg7G1LGHgZm2cJVH0xSuTKY4WpaX1RX5iWhVgz0dSrDNkjOi3hlZJ49VzXZi8T
abQ+Uiqfu7QIWvSOmjhxdrl8FuC2ZpZLRWB93VFNGKM1qYrjphTW1uMCnU4WtkD0
e8d9M3VCC/uL72a0+0R2El80W55yZxvHD6JHlp82Gdn+sqsAEnUcg5c+USLcehgc
v931SE4VfcxW4PELcOp0gKb00Q4bWp09Is/NVR+kVUax5EaT2Wo5oe7cx8OgrzMs
5daypwibGoFCloFE4LH59hbahoM5s8Ya59YAYEBWA1u68DCBYqS5AQsETaTH1gEI
AMCvsxsZcm5kIdbXATuPJQ1PKDejeeF0t4f27vDqR9SOZKas2ExpWbmjSeF9EdJ5
e1QKm88/WSXeUuE4Sn2xBfZXfRvhWndQIs6pOi8licuht1WiuYWBlvPefMO1e15G
fHrghhQ4RcTLIG8SWDcVFbsnr92RDy8NV7qZd/N08dZ+IS7z2JV8hp8Ts0a9T01q
p1h6lCbr9CAABQmlihMQ+hM4LO9DjqDeR85I1NWNpODeoks8zx86S9xOlEBxmWg2
G90ahBxm3IsYW9KtDd+oro6s6GQroo+5C0bcHyVL6Dylmhkn4CYQ99WAzMtw3gQ8
IDFrLO7Vw1kMc+vR4LGMPpkABimJAR8EGAECAAkFAk2kx9YCGwwACgkQmukW0AIt
N0ZBxggAqLFdkvVHgsK8wx6WyZalRpimirVQxStPT8WWlGyg8zJIGxbAzC6f4Fml
IuXGQYse+D8LfL7leWCyGu71+IZb22XCQ7ZO2ogWl6txUO5jWgPkoPBi0XMK/tLu
pT1WP8/lzrh1XQgwvLME+dcTMeuqgV5uDUmhn2OX9j8N3iyZeOV84cFrYmgrNfHn
lHlGx2kyXhgUwfK6noIUljprImIjlntGL0sh6lxTiu5bWMpXJVeJdRjNXuf4G+2/
mLU7BfsNP5jDr3+isTTLcOlZR0PHiAywIFPA2VOMTscB6nNEHiu8FLNFnY+QgboT
uC4E2095FYSXRMXc/rif7+d5+NApAg==
=+Q6Q
-----END PGP PUBLIC KEY BLOCK-----
This need to be converted to RSAKeyValue XML format like the one below.
<RSAKeyValue>
<Modulus>151Vw6Kq51bpblahblahblahWCkPmD345ncclzFrmeb9fjpE+B6VsIAqP3kahu00p33BmFpDpXOcgi uRC2En0HoGqyZB9ifvNlPhT7flZ1uVuC9JtywG3FmYRsqbpJuT7VTIAHYExU6T/FyRuyIbhdUVwhue7ARaObsEWszE1IoM7+XcYYs=</Modulus>
<Exponent>AQAB</Exponent>
<P>+7OU6YErvfk84O/2bX5s2753xE+pdiyJl3j8gy4zMh4UKeKVhyxgSJJCvNTl9XVIya6xnCWPkHVy8ylNqyLiHQ==</P>
<Q>20v7/nODe/jKcbsiYEwzGXp0ZyiG17JqzOBKrDFu6bwPiAEHzZln0CYfUn0E hJdCmetaq9WFXk27mbK3Z6aBxw==</Q>
<DP>RpoN6B26dlRGFlZZCkePI2he0esCfsppYdqnAejgpWs4 frM2/C6fZ1vXan5RbrOSUjg1S7ZhecToFHr+dUWYpQ== </DP>
<DQ>ayaHbF7YwfxgPo3nUBIrbamNstH0gPQmhxCYq+ZDgWV oWVXXkDgCp4vZBh8b1AVsf3SmCwv7bIaiwYi3YKfVBQ==</DQ
<InverseQ>YjvieMYPEDdUHqSZRWAsFlEESd3NczFCrbBaS HzbsbOZeDrccvUfoOP2jeIhuMt0qQwMqXY/NJau9Ldwdx7xNw==</InverseQ>
<D>wEg6bM5LVSWoQgWpoldz5dQWfQjqQVc8WzKpDA HQ8ejsm0ug4ZwRcRRnKZs3o7OTsiAJCfjqT9ij1gKMoeLrXIM1 RDOcAYBxI0qwWdRrr3sbar7ntMYmy4tlTTf8/G+ypw+CJi1gz2umswcx4MHhNTUGTUHSmjEjDTFfh3sW64k= </D>
</RSAKeyValue>
While generally it is possible to extract key material from an OpenPGP RSA key (note that OpenPGP also gets use of DSS/Elgamal keys), your example is somewhat inconsistent, as it states public OpenPGP key and private RSA/XML key. So I think it makes sense to sort these things out before you continue (as it is impossible to get private RSA key from a public RSA key).
Finally got what I was looking for from this SO post.
BouncyCastle RSAPrivateKey to .NET RSAPrivateKey