I have a RSA public key stored in a file. The file is plain text and reads as:
-----BEGIN RSA PUBLIC KEY-----
Mdflkjlkf8u8f84rkrjfgk8r7u8t758tkjfedfkjldfjhfjdfdjfkdjfkdfdfdfs
Ddfldkfdjfkjljfldfdlfhkdhfkdhfkdhfkdhfkjdffdnodhmndhiufdofhodhfd
xWCnQ6QOIwKqRY6lklk09erjbds6erottgkjirt895t5tixaevJlMmrZGLaITW66
xVjbPvdpjMniFiemtwIDAQAB
-----END RSA PUBLIC KEY-----
I want to feed this public key to an object of RSACryptoServiceProvider and encrypt an arbitrary message using Encrypt. Unfortunately, I couldn't figure out how to set PK. Any help is more than welcome.
X509Certificate2 cert = new X509Certificate2("pub_key.crt");
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
Then use rsa object.
Related
I have a private key that was generated by running:
openssl req -new -sha384 -x509 -days 63524 -subj "/C=CA/ST=State/L=City/O=Org/CN=Org-CA" -extensions v3_ca -keyout Org-CA.key -out Org-CA.pem -passout pass:pass1234
I need to use this private key and certificate to sign client CSRs. I used to use OpenSSL to do this, but I need to do this in C# instead.
I've found examples of what I want to do online, but those examples are using bouncy castle and an un-encrypted private key.
The key file looks like this:
-----BEGIN ENCRYPTED PRIVATE KEY-----
....
-----END ENCRYPTED PRIVATE KEY-----
And I'm trying to load it by doing this:
var privatekey = File.ReadAllBytes(#"Org-CA.key");
var rsaKeyParameters = PrivateKeyFactory.DecryptKey("pass1234".ToArray(), privatekey);
But thats not working. I get an error saying Wrong number of elements in sequence (Parameter 'seq')'
Is this how I'm supposed to be decrypting and loading the private key?
Depending on your .NET version, you may not need BouncyCastle at all. As of .NET Core 3.1 there is RSA.ImportEncryptedPkcs8PrivateKey() for DER encoded encrypted private PKCS#8 keys and as of .NET 5.0 there is even RSA.ImportFromEncryptedPem() for PEM encoded encrypted keys.
Otherwise with C#/BouncyCastle the import of an encrypted private PKCS#8 key is available e.g. with:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
...
string encPkcs8 = #"-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----";
StringReader stringReader = new StringReader(encPkcs8);
PemReader pemReader = new PemReader(stringReader, new PasswordFinder("<your password>"));
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
...
with the following implementation of the IPasswordFinder interface:
private class PasswordFinder : IPasswordFinder
{
private string password;
public PasswordFinder(string pwd) => password = pwd;
public char[] GetPassword() => password.ToCharArray();
}
If necessary, a conversion from keyParams to System.Security.Cryptography.RSAParameters is possible with:
using Org.BouncyCastle.Security;
...
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(keyParams);
which can be imported directly e.g. with RSA.ImportParameters().
Edit:
After digging through the C#/BouncyCastle source code, your way is also possible (which I didn't know before).
DecryptKey() has several overloads, one of which can handle the DER encoded encrypted key as already suspected by Maarten Bodewes in his comment. The latter can be easily generated with the Org.BouncyCastle.Utilities.IO.Pem.PemReader() class.
Note that this PemReader is different from the one in the first implementation (different namespaces), which is why I use the namespace explicitly in the following code snippet. With this approach, the RsaPrivateCrtKeyParameters instance can be generated as follows:
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
...
StringReader stringReader = new StringReader(encPkcs8);
Org.BouncyCastle.Utilities.IO.Pem.PemReader pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pem = pemReader.ReadPemObject();
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.DecryptKey("<your password>".ToCharArray(), pem.Content);
...
pem.Content contains the DER encoded encrypted private PKCS#8 key.
I generated an RSA Public Key in C# with RSACryptoServiceProvider with a key size of 512-bits.
// Generate a public/private key using RSA
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(512);
// Read public key in a string
string str = RSA.ToXmlString(true);
Console.WriteLine(str);
// XML output
<RSAKeyValue>
<Modulus>26+2lMVkqh99O3nBV/EU5KkO3FYx7R/0Fnlh6FES2wvIAZ6yaHrAhrX6a/BWag4N/XRH3z+nxQBRiG1pPUDQKQ==</Modulus>
<Exponent>AQAB</Exponent>
<P>9y6RKpt6OsQFoPfsxo9+NpSJF2xrWg9+h+f6ri7svv8=</P>
<Q>44YJ0p9qDJuvIM+PaFi9p8NpYerSUAfO49camwH7mNc=</Q>
<DP>Zw9ebXJn8yqZ4jSc32kiyaUCx+ZnmCRPsGCzr35XLYc=</DP>
<DQ>Gn4OAL9dKtCp6KkiaqUCmFkxmRwtlvIBzhfK2ke10ws=</DQ>
<InverseQ>6bwnrFXgBggw2+9fyaPP4femrMH3VQgxBkj8P1B3TTs=</InverseQ>
<D>SJpisfomkZ7EiZJsln7DU+qXUbRe3aowxfippdidbaxzM3+lMgLH2V8q9Ba7M8leG8mxkvttdilojclFFi9q3Q==</D>
</RSAKeyValue>
I throw the Modulus value into a PEM file:
-----BEGIN PUBLIC KEY-----
26+2lMVkqh99O3nBV/EU5KkO3FYx7R/0Fnlh6FES2wvIAZ6yaHrAhrX6a/BWag4N/XRH3z+nxQBRiG1pPUDQKQ==
-----END PUBLIC KEY-----
OpenSSL is not able to decode the Public Key, however. Here is the command I used:
openssl rsa -pubin -inform PEM -text -noout < public-key.pem
And here is the result with errors:
unable to load Public Key
30844:error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long:crypto\asn1\asn1_lib.c:101:
30844:error:0D068066:asn1 encoding routines:asn1_check_tlen:bad object header:crypto\asn1\tasn_dec.c:1137:
30844:error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:crypto\asn1\tasn_dec.c:309:Type=X509_PUBKEY
30844:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:crypto\pem\pem_oth.c:33:
Why can't OpenSSL decode this Public Key? I even tried the default key size of 1024-bits and it still couldn't read it. Is there a compatibility issue between OpenSSL and the RSACryptoServiceProvider class? How can I get OpenSSL to properly read and decode this Public Key?
I only have a private key in a pem file and need to extract the public key from it.
At the moment I'm using openssl for this:
openssl rsa -pubout -outform DER
Ho can I do the same in c# (and Bouncycastle)?
In windows world file that contains private key is called PFX.
So you can try to import certificate to collection using .net System.Security.Cryptography infrastructure:
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(filePath, password, X509KeyStorageFlags.PersistKeySet);
The code block was taken from here:
How to retrieve certificates from a pfx file with c#?
If your .pem file already contains the public key it is usually between the Lines
-----BEGIN PUBLIC KEY-----
and
-----END PUBLIC KEY-----
of the pem file.
At least Openssl does it like this, but you should check it carefully.
See this link for an overview over the structure of a pem file.
So you could get it by using the Substring method. This should work:
static void ExtractPublicKeyFromPem(string path)
{
var startText = "-----BEGIN PUBLIC KEY-----";
var endText = "-----END PUBLIC KEY-----";
var pem = System.IO.File.ReadAllText(path);
var start = pem.LastIndexOf(startText);
var end = pem.IndexOf(endText);
var publicKey = pem.Substring(start, end - start);
}
Hope it helps :)
Without BounceyCastle.
I have my cert, and the GetPublicKey() value is not what the Java side of the house needs.
The cert if an X509Certificate2 object, using DSA encryption. Created using makecert
Convert.ToBase64String(cert.GetPublicKey()) returns
AoGAeaKLPS4ktxULg3YQL0ePphF08tKsddZtv3SDERa8b8go5h3AxmWjuDd8y9dIzZFe8KDjY9Lg
JU4JOA27snO3fCsPAVkmJ0O2pbxn+wzT7oij2FOLcCAjnFNNsoaWrtMv+I4XXl18DyDQLFkZiPx9
2UyuDzoQTGxgCrPccQPjUgY=
Convert.ToBase64String(cert.RawData) returns
MIICxjCCAoagAwIBAgIQbdIpaaU9rZdA+wJKA+mUfDAJBgcqhkjOOAQDMBYxFDASBgNVBAMTC0RT
QSBSb290IENBMB4XDTEzMDExMDE3MTAzNVoXDTM5MTIzMTIzNTk1OVowFDESMBAGA1UEAxMJVXNl
ciBOYW1lMIIBtzCCASwGByqGSM44BAEwggEfAoGBALWn4Iyvn7LFkV9ULoZtwJ8J1c+ibsbhjPiw
+xUgRW2LAZV2/Lv89W1jCprNkf87tN/ogMT/1VSIOo7ff/tqVRTWPVJ1ZMrR9VOnF2k/Sorg8Cmr
sAClsSWrACKIwK2XKJGWTU4oMLxvYcu85+yQ4nWLofgA/+WARrJ/rk2aUSZ3AhUAlqPLNh6JZkpD
G/OXKzhsZFUiDrkCgYEAoICjHltWOgN8/2uyAaMTNrBuJfi/HM9AWe5B8m9HDfl1K6Qx2Ni6tbYP
uFtvHdGnoqqn46l7eY+xjpi5GEydvkPtAQKmTDGcSh6vtnTeNV15Hafg5pXUKw1OisIr/bpx/KIk
cgCtSo6qC5IhDzeZXnfJYcE+8U+O6hEr5dwByN4DgYQAAoGAeaKLPS4ktxULg3YQL0ePphF08tKs
ddZtv3SDERa8b8go5h3AxmWjuDd8y9dIzZFe8KDjY9LgJU4JOA27snO3fCsPAVkmJ0O2pbxn+wzT
7oij2FOLcCAjnFNNsoaWrtMv+I4XXl18DyDQLFkZiPx92UyuDzoQTGxgCrPccQPjUgajWTBXMAwG
A1UdEwEB/wQCMAAwRwYDVR0BBEAwPoAQmhMLkJ/cPXGitvGMB81tZaEYMBYxFDASBgNVBAMTC0RT
QSBSb290IENBghDCpMJ75zgZokJVlZmNq/LTMAkGByqGSM44BAMDLwAwLAIUYUALM9WhgwzRMj1y
MSdoparmYvICFFxLgFr2ow3NGTkqWvHIXtjO9R0G
However, when my Java counterpart gets the public key, using the same cert file, gets
$ cat david-509.cer | openssl x509 -pubkey
-----BEGIN PUBLIC KEY-----
MIIBtzCCASwGByqGSM44BAEwggEfAoGBALWn4Iyvn7LFkV9ULoZtwJ8J1c+ibsbh
jPiw+xUgRW2LAZV2/Lv89W1jCprNkf87tN/ogMT/1VSIOo7ff/tqVRTWPVJ1ZMrR
9VOnF2k/Sorg8CmrsAClsSWrACKIwK2XKJGWTU4oMLxvYcu85+yQ4nWLofgA/+WA
RrJ/rk2aUSZ3AhUAlqPLNh6JZkpDG/OXKzhsZFUiDrkCgYEAoICjHltWOgN8/2uy
AaMTNrBuJfi/HM9AWe5B8m9HDfl1K6Qx2Ni6tbYPuFtvHdGnoqqn46l7eY+xjpi5
GEydvkPtAQKmTDGcSh6vtnTeNV15Hafg5pXUKw1OisIr/bpx/KIkcgCtSo6qC5Ih
DzeZXnfJYcE+8U+O6hEr5dwByN4DgYQAAoGAeaKLPS4ktxULg3YQL0ePphF08tKs
ddZtv3SDERa8b8go5h3AxmWjuDd8y9dIzZFe8KDjY9LgJU4JOA27snO3fCsPAVkm
J0O2pbxn+wzT7oij2FOLcCAjnFNNsoaWrtMv+I4XXl18DyDQLFkZiPx92UyuDzoQ
TGxgCrPccQPjUgY=
-----END PUBLIC KEY-----
And thus my problem. How do I get this value from my cert?
Thanks!
You should use cert.PublicKey.EncodedKeyValue instead of cert.GetPublicKey().
EncodedKeyValue provides ASN1 encoded value, not raw key data as GetPublicKey().
So you can use this code
void ExportPublicKey(X509Certificate2 cert, string filePath)
{
byte[] encodedPublicKey = cert.PublicKey.EncodedKeyValue.RawData;
File.WriteAllLines(filePath, new[] {
"-----BEGIN PUBLIC KEY-----",
Convert.ToBase64String(encodedPublicKey, Base64FormattingOptions.InsertLineBreaks),
"-----END PUBLIC KEY-----",
});
}
See Ian Boyd's answer. It just provides all the answer that you're looking for about encoding. Note that it's related to RSA and not DSA, but it gaves you all the informations about PEM/DER/ASN.1 encoding, which is your problem here.
I'm using RSACryptoServiceProvider I've generated public key and private key. The keys generated by it are in the following format:
Public key:
<RSAKeyValue>
<Modulus>m9bAoh2...eGNKYs=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Private key:
<RSAKeyValue>
<Modulus>m9bAo...ZAIeGNKYs=</Modulus>
<Exponent>AQAB</Exponent>
<P>xGj/UcXs...R1lmeVQ==</P>
<Q>yx6e18aP...GXzXIXw==</Q>
<DP>NyxvnJ...1xAsEyQ==</DP>
<DQ>La17Jycd...FhApEqwznQ==</DQ>
<InverseQ>JrG7WCT...Hp3OWA==</InverseQ>
<D>RdWsOFn....KL699Vh6HK0=</D>
</RSAKeyValue>
but using PGP Desktop i've generated keys like this -
Public key:
mQCNBEoOlp8BBACi/3EvBZ83ZduvG6YHu5F0P7Z3xOnpIsaPvTk0q+dnjwDUa5sU
lEFbUZgDXSz7ZRhyiNqUOy+IG3ghPxpiKGBtldVpi33qaFCCEBiqsxRRpVCLgTUK
HP2kH5ysrlFWkxTo
=a4t9
Private key:
lQHgBEoOlp8BBACi/3EvBZ83ZduvG6YHu5F0P7Z3xOnpIsaPvTk0q+dnjwDUa5sU
lEFbUZgDXSz7ZRhyiNqUOy+IG3ghPxpiKGBtldVpi33qaFCCEBiqsxRRpVCLgTUK
waBnEitQti3XgUUEZnz/rnXcQVM0QFBe6H5x8fMDUw==
=CVPD
So when I'm passing the keys generated by PGP Desktop it is able to do encryption and decryption perfectly but when im passing the keys generated by RSACryptoServiceProvider I'm not able to encrypt and decrypt?
Can anyone please tell me how to generate keys in the pattern generated by PGP?
using the bouncycastle c# library this is how i generate key pairs.
public void GenerateKey(string username, string password, string keyStoreUrl)
{
IAsymmetricCipherKeyPairGenerator kpg = new RsaKeyPairGenerator();
kpg.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(0x13), new SecureRandom(), 1024, 8));
AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair();
FileStream out1 = new FileInfo(string.Format("{0}secret.asc", keyStoreUrl)).OpenWrite();
FileStream out2 = new FileInfo(string.Format("{0}pub.asc", keyStoreUrl)).OpenWrite();
ExportKeyPair(out1, out2, kp.Public, kp.Private, username, password.ToCharArray(), true);
}
private static void ExportKeyPair(
Stream secretOut,
Stream publicOut,
AsymmetricKeyParameter publicKey,
AsymmetricKeyParameter privateKey,
string identity,
char[] passPhrase,
bool armor)
{
if (armor)
{
secretOut = new ArmoredOutputStream(secretOut);
}
PgpSecretKey secretKey = new PgpSecretKey(
PgpSignature.DefaultCertification,
PublicKeyAlgorithmTag.RsaGeneral,
publicKey,
privateKey,
DateTime.Now,
identity,
SymmetricKeyAlgorithmTag.Cast5,
passPhrase,
null,
null,
new SecureRandom()
// ,"BC"
);
secretKey.Encode(secretOut);
secretOut.Close();
if (armor)
{
publicOut = new ArmoredOutputStream(publicOut);
}
PgpPublicKey key = secretKey.PublicKey;
key.Encode(publicOut);
publicOut.Close();
}
and it generate private and public keys in armored ASCII format such as.
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.32
mIsEShU7ywEEAKtxKTtGTyUaVxFuWBpziA2l7qDKhe6jznre3DMPuzDnN4Ax573a
7s/bPOkzkK9tEUGFw+BW6F4DkKydv8SQfSN5Vvc0RFMha8X1E8jki1oXTIPA8bKK
dg8ZewZt8+Zwpt5IPAkIydmxDhMjwd71ay3p1ypOfROFPOfc2dBPx/0JAAUTtAdo
YW1zbWFuiJwEEAECAAYFAkoVdAsACgkQEz/ESPB1tojuIQP8CjAzJx8PoIN33pxQ
AfGF+fMCZx8/m7dDBE113aiio25BCvNKOpFwye2UK4ioKN70k24pzkyi8AZO22/s
u6GL7XEiiBZLPynBxJR4A7PzvD3KNqdQUqesu9IkPFyXz3UFH3clR0hnZtZtgnbk
L9dvj5RYVuGiS3Dcf1zoLMOiCdc=
=dFfG
-----END PGP PUBLIC KEY BLOCK-----
RSA and PGP are different.
What you are essentially asking is how do I run my petrol car on diesel? The answer is you can't.
You should look into PGP CommandLine - this allows you to perform PGP commands from the command line.
thanks for the good methods, was faffing around the web for them since quite a while,
some correction tho...
After a call to
ExportKeyPair(out1, out2, kp.Public, kp.Private, username, password.ToCharArray(), true);
please also call
out1.Close();
out2.Close();
A sample private key generated from the above routine
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: BCPG C# v1.6.1.0
lQHqBEvhYOcBBACa5HjZ14ULRvZLKj+rwscQh63Zd9rnfZmVKN5fNsK+ocxxV8rc
TWa6P3knIWCra1lqZ1onNtM8tL1XRuK9pagu7XPjjHUR37ajYv/e0/w9IHlOBtM4
1T3oMM48FBH5WGZswOQnTCHoHpZilx7zeJgcyoUayM9uaWGWVLb6PqAE2QAFE/8D
AwL4BEE4Y+mD6GB0ctnbN4tZuctydODYJUNOK3p+UdgEh5rj5nOelB5h3kqFWdwq
8F3EBxB4guRnLOxIeSjzvFoFpqb4QSrM4brdisaHoK0qgrsyCaQhdepVlz662hSD
5bxJPQqmJ4c9X7x6tTMwDAseoz+VUK9U91iN9jv72hp2dXJYjJvBbkTX9euz2i4P
HrlLV2DuOeGWipsb+sIOuYqpZoK5aMq5AAGTTmmApYam/+0d2lXTt+Cw0FkoN1U+
CWriVFE+x58/MmpqKttlrHyp/c7aRmdxeKheY7QAnKPmTRFZAu6HR/DnLSya6+Qz
MqsYBZAocDs7Bg9U6N79Ynl7mkChoVR4ZwJE/OXgHDRSYlcBwT++frHI06cn1l8t
f0CWU/Z6EuYj/rmgL/v5ln7EQ2H+SRypPxYJKK8dTcu2uk5Ev1x4EOQ2nF1BxBB1
CceSVEimc2TDcVj0K7QIc3BhcmVraDOInAQQAQIABgUCS+FS1wAKCRBHXl7hbFhY
BLInA/9TPwmivxofs7/y4xmrl0X+Ruish0l+KC45/MQGU1bT2MYCR5jefyaUIHMK
gH8bX4DHobBFxtkTXoTgpZmm5JNHGiSQoOXqW7iqHOkp6q6rkwV9BYvfsbjMsfAB
bV7l29CMoMDj1qB3k9CJDP4MuorEI5Qx30x07Mm2+uwcYC2+Ag==
=jh9t
-----END PGP PRIVATE KEY BLOCK-----
thanks once again for the above methods.
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG C# v1.6.1.0
mIsES+Fg5wEEAJrkeNnXhQtG9ksqP6vCxxCHrdl32ud9mZUo3l82wr6hzHFXytxN
Zro/eSchYKtrWWpnWic20zy0vVdG4r2lqC7tc+OMdRHftqNi/97T/D0geU4G0zjV
PegwzjwUEflYZmzA5CdMIegelmKXHvN4mBzKhRrIz25pYZZUtvo+oATZAAUTtAhz
cGFyZWtoM4icBBABAgAGBQJL4VLXAAoJEEdeXuFsWFgEsicD/1M/CaK/Gh+zv/Lj
GauXRf5G6KyHSX4oLjn8xAZTVtPYxgJHmN5/JpQgcwqAfxtfgMehsEXG2RNehOCl
mabkk0caJJCg5epbuKoc6SnqrquTBX0Fi9+xuMyx8AFtXuXb0IygwOPWoHeT0IkM
/gy6isQjlDHfTHTsybb67BxgLb4C
=ZyOZ
-----END PGP PUBLIC KEY BLOCK-----