I have a sample C# script that handle decryption. By using the key provided with the sample, the code is working fine:
public static string DecryptByPrivateKey(string s, string key)
{
s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
engine.Init(false, GetPrivateKeyParameter(key));
byte[] byteData = Convert.FromBase64String(s);
var resultData = engine.ProcessBlock(byteData, 0, byteData.Length);
return CommonHelper.EncodeBase64(resultData);
}
private static AsymmetricKeyParameter GetPrivateKeyParameter(string s)
{
s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
byte[] privateInfoByte = Convert.FromBase64String(s);
AsymmetricKeyParameter priKey = PrivateKeyFactory.CreateKey(privateInfoByte);
return priKey;
}
However, when using our own private key, above function will throw exception at PrivateKeyFactory.CreateKey():
System.ArgumentException: 'Unknown object in GetInstance:
Org.BouncyCastle.Asn1.DerInteger Parameter name: obj'
Our public/private key strings are generated with openssl on windows with command line:
openssl pkcs12 -in cert.pfx -nocerts -nodes -out cert.key
openssl rsa -in cert.key -out cert_private.key
openssl rsa -in cert.key -pubout -out cert_public.key
The key strings are in base64 format. The sample private key have 1624 characters while our private key has 1592 only.
In Visual Studio debug mode I checked the parameter privateInfoByte is a "byte[1192]". I have no clue on the error.
I am not sure my key strings are in correct format. How can I verify? Thank you.
According to the documentation, PrivateKeyFactory.CreateKey() expects a private key in PKCS#8 format. However, cert_private.key has the PKCS#1 format, which is thus incompatible.
cert.key, on the other hand, contains among other things the private key in PKCS#8 format, which can be extracted from this. Alternatively, the PKCS#1 formatted key can be converted to a PKCS#8 formatted key using OpenSSL, see openssl pkcs8:
openssl pkcs8 -topk8 -nocrypt -in <pkcs#1 key file> -out <pkcs#8 key file>
cert_private.key and cert.key contain the PEM encoded keys consisting of header, footer and the Base64 encoding of the DER encoded keys.
PrivateKeyFactory.CreateKey() expects a DER encoded key, i.e. from the PEM encoded key determined e.g. from cert.key, header, footer and line breaks are to be removed and the rest has to be Base64 decoded (which seems to be already considered in your code).
Header/footer of the PEM encoded PKCS#8 key are -----BEGIN PRIVATE KEY-----/-----END PRIVATE KEY----- and of the PEM encoded PKCS#1 key -----BEGIN RSA PRIVATE KEY----- / -----END RSA PRIVATE KEY-----.
Edit:
The statement openssl pkcs12 -out... -in... parses a PKCS#12 file and writes the contained certificates and private keys PEM encoded to a file. -nocerts causes the contained certificates not to be written, -nodes causes the private keys not to be encrypted (i.e. your statement exports only the PEM encoded private key unencrypted). The format of the exported private key is not explicitly specified, but can be easily identified as PKCS#8 using header/footer or an ASN.1 parser.
The statement openssl rsa -in... -out... finally exports the PKCS#8 formatted key to a PKCS#1 formatted key (PEM encoded). So to get the needed PKCS#8 formatted key you have the option to 1st use the PKCS#8 formatted key exported directly from the PKCS#12 file (s. previous section) or 2nd convert the PKCS#1 formatted key back to a PKCS#8 formatted key.
Therefore: yes, cert.pfx is a file in PKCS#12 format, and yes, cert_private.key is a key in PKCS#1 format, which is incompatible with PrivateKeyFactory.CreateKey(), which requires a key in PKCS#8 format.
Related
I have an encrypted base64 file "PersonalCodes.txt" and a private key to it "private.key". The key is in .pem (---begin private key -- etc.) and is encrypted with -gost89.
I need to use an OpenSSL.NET for this (apparently System.Security.Cryptography have no support on .pem keys) For the simple openssl client , the commands will be:
base64 -d -in "PersonalCodes.txt" -out "PersonalCodesOUT.txt"
smime -decrypt -in "PersonalCodesOUT.txt" -inform der -inkey "private.key" -out "DecryptedCodes.txt"
First one is decrypting from base64 -ok. easy. Next one is decrypting with gost89 key.
As for the .NET - honestly , i'm completely frustrated. I added a reference to an openssl wrapper, and found an example how to get a key from file :
byte[] b = System.IO.File.ReadAllBytes(#"D:\private.key");
OpenSSL.Core.BIO bio = new OpenSSL.Core.BIO(b);
OpenSSL.Crypto.CryptoKey key = OpenSSL.Crypto.CryptoKey.FromPrivateKey(bio, "");
But this gives me an exception : unsupported private key algorithm According to google - i need to help openssl to see gost89
How should i do that in c#?
Moreover, can anyone help me with a the last command - decrypting with a private key in openssl.net? Ty...
-------------------------------------------------------------------------------
Found this implementation of the gost89 :
https://github.com/embedthis/packages/blob/master/openssl/openssl-1.0.1c/engines/ccgost/gost89.c
However it also doesnt give a function to decrypt a file with key...
Currently I perform this operation through openssl, and I have had no problem with the generated file
openssl dgst -sha256 -sign privateKey.key -out file.txt.signature file.txt
Now, we want to automate the generation of the file using C #, but I have not been able to get the same result.
public class Program
{
static void Main(string[] args)
{
Console.WriteLine(CreateToken("key...", "text"));
Console.ReadLine();
}
public static string CreateToken(string key, string message)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(key);
HMACSHA256 hmacsha256 = new HMACSHA256(keyByte);
byte[] messageBytes = encoding.GetBytes(message);
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return System.Text.Encoding.UTF8.GetString(hashmessage);
}
}
I'm new to working with this, what would be the right way?
Am I not retrieving the information properly ?, Should I get the content directly from the file?
Thank you very much.
Signature generation is not the same thing as HMAC message authentication and it uses a different key. As HMAC can use a key of any size, it will probably take the private key, but that's not how it is supposed to work. RSA is an asymmetric algorithm that uses private and public keys, MAC uses symmetric, secret keys. The dgst -sign instead uses RSA PKCS#1 v1.5 padding to sign the file.
From the OpenSSL Wiki on dgst:
When signing a file, dgst will automatically determine the algorithm (RSA, ECC, etc) to use for signing based on the private key's ASN.1 info. When verifying signatures, it only handles the RSA, DSA, or ECDSA signature itself, not the related data to identify the signer and algorithm used in formats such as x.509, CMS, and S/MIME.
HMAC is not the same thing as SHA-256 either. RSA signature generation uses a hash, not a HMAC. You should use the SHA256 class to create a hash. HMAC is a message authentication code build using the SHA-256 hash. However, the SHA class is not needed as signature generation usually includes the hash generation (you sign a message, not a hash value).
So to create a signature, take a look at the RSAPKCS1SignatureFormatter class, it includes an example at the bottom. Try again using this example.
Make sure your message only contains ASCII (both in the text file as in your string) or your result may fail as well.
I want to verify a private key signed SHA256 hash using the CryptographicEngine in a UWP application. The hash is created externally and is signed with a private RSA key with passphrase. For this example however, I also generate the unsigned hash. Both hashes are then compared at the end to verify that they are the same.
I have created my private and public keys using OSX command line, specified in this blog.
This gave me two .pem files. My public key has the following structure:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3fasaNKpXDf4B4ObQ76X
qOaSRaedFCAHvsW4G0PzxL/...ETC ETC
-----END PUBLIC KEY-----
Here is my C# code to decrypt the hash:
//HASH THE INPUT STRING
var inputText = "stringtohash";
// put the string in a buffer, UTF-8 encoded...
IBuffer input = CryptographicBuffer.ConvertStringToBinary(inputText,
BinaryStringEncoding.Utf8);
// hash it...
var hasher = HashAlgorithmProvider.OpenAlgorithm("SHA256");
IBuffer hashed = hasher.HashData(input);
// format it...
string ourhash = CryptographicBuffer.EncodeToBase64String(hashed);
Debug.WriteLine(ourhash);
//CONVERT EXTERNAL HASH TO BUFFER
IBuffer data = CryptographicBuffer.DecodeFromBase64String("b18fbf9bc0fc7595af646155e18b71e1aeccf01719f9f293c72217d7b95cc2106edb419078c4c5c1c7f7d106b90198a4f26beb49ff4a714db4bface1f94fff193b8126ce05fe13825144a3dde97f55399846b6fd768f1fb152f1ba71bbf5cde8c1a7e58621a493070256e2444db36c346a88e870906529cf13c072ead50b6a01b2e74c7ef8c5d423e8ea25220f524b563ae2c3345b7837f9cd1a357540b1380c86287b9a240cf67f7518f11418352b665b657c5ffb6cbcb6126ec59e360de6304392b78cf4de79b52d73b8292df6a1e643d0c0f0945aae5949b391e2915772c996f03e6d1879192b7edf0f40c01b875e768358aa47a992070f628418ddf06472");
//CONVERT PUBLIC KEY TO BUFFER
IBuffer publickey = CryptographicBuffer.DecodeFromBase64String("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3fasaNKpXDf4B4ObQ76XqOaSRaedFCAHvsW4G0PzxL / RuAQFz80esZPyyDCps1PAbTKzQ + QblChPo7PJkbsU4HzNN4PIRGh5xum6SRmdvOowrlTUtyxdOkRJoFxmiR / VCea + PUspt26F7PLcK9ao5 + hVzMvPuqdYenqzd01f1t5hQEhFQ9qjB6Es8fpizHd / RSRfZ7n6rVKm9wYfCRLB7GJ7IHhWGuZrx9fjzsbW8eagu06qRhnUuR5oDVjXC8ZeazsRiw50xMuOzkhX9Oo081IYikwCgseJmQhT7vF4lZoyeB4qJpwTCA + glSy1w9N8ZfxyXK8QaT2RsrBrzl0ZCwIDAQAB");
// Open an asymmetric algorithm provider for the specified algorithm.
AsymmetricKeyAlgorithmProvider rsa = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// Import Key
CryptographicKey key = rsa.ImportPublicKey(publickey, CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo);
// Decrypt the Hash using our Key
IBuffer result = CryptographicEngine.Decrypt(key, data, null);
Debug.WriteLine(result.ToString());
//Compare the two hashes
if (data == result) {
//Hash is verified!
}
Unfortunately when reaching the Decrypt method I get a NotImplementedException with error
The method or operation is not implemented
I researched online and I understand what needs to happen in theory but I don't know how to debug this further. What can I try?
Although both called PKCS#1 v1.5 padding, the padding for signature generation and encryption is not identical, see RFC 3447 for more details.
If you look at the RsaPkcs1 property you can see it is aimed at encryption:
Use the string retrieved by this property to set the asymmetric algorithm name when you call the OpenAlgorithm method. The string represents an RSA public key algorithm that uses PKCS1 to pad the plaintext. No hash algorithm is used.
As I don't see any option for "raw RSA", i.e. RSA without padding, it seems you are only able to verify your signature. However, RSA decryption expects an RSA private key. It's very likely that you get the error because of this: if you try and decrypt with a public key it will fail.
If you want to precompute the hash you can use VerifySignatureWithHashInput.
For other functionality you may have to use e.g. the C# lightweight API of Bouncy Castle. In the end you don't need platform provided cryptography to verify a signature.
I have a der file 'text.der' that contains a DER-encoded key. I want read it and convert to an instance of AsymmetricCipherKeyPair from the Bouncycastle C# library (here are the javadocs for the Java version).
For example for a pem file, we have PemReader/Writer in bouncycastle and we can do it.
How can I go from the encoded key in a file to an AsymmetricCipherKeyPair
Assuming its the usual binary format DER public key file, with the binary DER coding for the SubjectPublicKeyInfo structure (I think OpenSSL uses this for its DER output format), you can do:
byte[] derKeyBytes = File.ReadAllBytes("text.der"); // read in the binary file
// Decode the public key component
AsymmetricKeyParameter publicKey =
PublicKeyFactory.CreateKey(derKeyBytes);
You're better of just using the AsymmetricKeyParameter (which is the public part of the key), but if you absolutely want it in a AsymmetricCipherKeyPair, you can do this:
// Put the public key into a keyPair, leave the Private key uninitialized.
AsymmetricCipherKeyPair keyPair =
new AsymmetricCipherKeyPair(
publicKey,
new AsymmetricKeyParameter(true));
The following command is use to make a keystore called myalias.p12 and export a certificate called myalias2.cer.
Java Keytool is a key and certificate management utility. It allows users to manage their own public/private key pairs and certificates.
E:\>keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -alias myalias2 -keystore myalias.p12 -storetype pkcs12 -dname "cn=www.myalias.com, ou=myalias2, o=myalias2, l=tp, st=tp, c=tw" -storepass 123456 -keypass 123456
E:\>keytool -export -alias myalias2 -keystore myalias.p12 -storetype pkcs12 -rfc -file myalias2.cer -storepass 123456
Encryption:
string input="hello";
X509Certificate2 myCertificate = GetCertFromCerFile("e:\\myalias2.cer");
RSACryptoServiceProvider provider1 = (RSACryptoServiceProvider)myCertificate.PublicKey.Key;
byte[] buffer1 = Encoding.UTF8.GetBytes(input);
byte[] result = provider1.Encrypt(buffer1, false);
string data= Convert.ToBase64String(result);
Decryption:
44. RSACryptoServiceProvider provider2 = (RSACryptoServiceProvider)myCertificate.PrivateKey;
45. byte[] buffer2 = Convert.FromBase64String(data);
46. byte[] result2 = provider2.Decrypt(buffer2, false); // <-- error here
47. String decryptedMessage = Encoding.UTF8.GetString(result2);
It can normally perform the encryption operations. But, I found some errors on Line 46, (performing the decryption):
A first chance exception of type
'System.NullReferenceException'
occurred in CertTest.exe The thread
'' (0xcc8) has exited
with code 0 (0x0). at
CertTest.Program.Decrypt(String data)
in
D:\vsworkspace\CertTest\CertTest\Program.cs:line
46 at
CertTest.Program.Main(String[] args)
in
D:\vsworkspace\CertTest\CertTest\Program.cs:line
29
Anyone have Idea? Because I don't know how to solve this problem.
Thanks very much!
The NullReferenceException you're getting is because PrivateKey is null. This is because .cer files only includes a single .X509 certificate, which only includes the public key.
In this case that means you can only encrypt data using the certificate. In order to decrypt it you'll need the private key.
You can get access to the private key using the .p12 (or .pfx) file. This PKCS#12 file includes (in general) both the private key (password protected) and the certificate(s).
There are several X509Certificate[2] constructor that will accept a password and automatically decrypt the private key. Once loaded from the .p12 file your code will receive a valid (non-null) RSACryptoServiceProvider instance and you'll be able to decrypt the data.
BTW you should not encrypt string (or data) this way using RSA :-)
For more details read http://pages.infinit.net/ctech/20031101-0151.html