I have implemented a simple one liner that generates the certificate thumbprint in java
X509Certificate cert = readCertFromFile(path); //I read the certificate here
byte[] digest = DigestUtils.sha256(cert.getEncoded()); //I calculate hash of the cert
I want to implement the same in C#. I have tried to use C#'s X509Certificate class to read the cert and generate the hash value but it is different from the value generated through java. Is there an implementation of java's X509Certificate.getEncoded() in C#? getEncoded() in java returns
the encoded form of this certificate. It is assumed that each certificate type would have only a single form of encoding; for example, X.509 certificates would be encoded as ASN.1 DER.
I appreciate any help on this.
The most straightforward way to get the SHA-256 thumbprint of a certificate is
byte[] thumbprint = cert.GetCertHash(HashAlgorithmName.SHA256);
or, if you want the hex version:
string thumbprint = cert.GetCertHashString(HashAlgorithmName.SHA256);
That's equivalent to the following code, which is more like the Java approach:
byte[] thumbprint = SHA256.HashData(cert.RawData);
the difference being that the GetCertHash routine doesn't need to make a copy of RawData value (which the property accessor does), so it's a bit more performant.
Related
I am working on an API which requires a header for API authentication. The header contains a hash string which is created using md5 algorithm and a secret key. I want to write a function like this:
public string CreateMD5Hash(string input, string secretKey)
{
return output;
}
I tried to use bouncy castle API. But I couldn't find proper documentation. That is why I couldn't make it use.
Okay BASICALLY let's break this into two simple concepts Cryptography and Hashing.
Cryptography
There are three fields secrete key a value and an encrypted value, and two methods.
Encrypt(value, secret-key) this method gets value and secret key and returns the encrypted value.
Decrypt(encrypted-value, secret-key) and this method gets encrypted value and secret key and returns the value.
like AES, DES, etc ...
Hashing
There are just two fields value and hashed value, and one method.
Hash(value) this method gets the value and returns the hashed value.
like MD5, SHA family, etc ...
Simple right!
So your question is not correct because MD5 is a hashing algorithm that usually is used for hashing passwords and comparing the hash of them.
Now I recommend taking a look at the API you are talking about for authentication.
You can share the link of documentation or an already encrypted header (if it is not sensitive data) to help you.
Update
According to the link, it is using HMAC with MD5.
This is the sample in the document, written in PHP.
$hash = hash_hmac('md5', $string, $key);
You can use this code for C#:
using System.Security.Cryptography;
using System.Text;
...
public string HashHmacMD5(string message, string secret)
{
Encoding encoding = Encoding.UTF8;
using (HMACMD5 hmac = new HMACMD5(encoding.GetBytes(secret)))
{
var msg = encoding.GetBytes(message);
var hash = hmac.ComputeHash(msg);
return BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
}
}
Your question is somewhat confusing.
For simple API authentication, you want to generate a secret that you want to share with the caller. This secret should be validated on your side.
Normally you would generate a random secure string with length about 50 - this is the secret. You share this secret with the caller - normally you warn the caller to securely store the value, since there is no way to recover it. Do not store this secret in the database.
You would generate a salt and hash the secret. You store both the salt and the hashed secret in the database. When the request comes in, you extract the value in the header, hash it with the salt and compare the result with the hashed secret in the database. User would be authenticated if they are match.
Is this more inline with your thinking? Is your question, how to hash it properly?
I'm creating an IAP plugin for the local Android market that I think has similar API as google play.
I build an Android side and it will return all response from the market to the unity c# side.
All part working fine but I can't figure out how to verify the signature of the response.
I'm new in cryptography and searching day to day about this.
Please correct me if I'm wrong.
They use a hash algorithm to sign data and encrypt that hash with private key.
I must decrypt signature with public key and compare hashes.
This is my public key (Base64):
MIHNMA0GCSqGSIb3DQEBAQUAA4G7ADCBtwKBrwDltnT/AaF3oMt+F3lza5JEvce0GLS1L9v9Z53lQ3LNluGk0eI2ukgWm7tAiIOLQgn11Sb9mW2VWkYTWGnZ1AZtY0GwdUQJUr7u3CWNznE6XH4UCVOVhGDCLnFrG8BcfDelhcfReGZQ3izOefhc4Oq6vZf5PfLwximK+FH27fR6XL8vg3yyK4LSwT764Dfd6H3IGes6EdTx/C3C690jdyMvhi2Q3qBiqfepHzW/jV8CAwEAAQ==
This key is in ASN.1 DER format.
I break it out and find this data:
SEQUENCE
SEQUENCE
OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption(PKCS #1)
NULL
BIT STRING
SEQUENCE
INTEGER 969837669069837851043825067021609343597253227631794160042862620526559…
INTEGER 65537
As I read on net first INTEGER is Modules and second INTEGER is Exponent
So in c# write a code like this =>
var parameter = new RSAParameters
{
Modulus = HexToByteArray(/* "first_INTEGET" */),
Exponent = BitConverter.GetBytes(/* "second_INTEGER" */)
};
Market send me a Json like this:
{"orderId": "0j8oJgE0Bett-neB", "purchaseToken": "0j8oJgE0Bett-neB", "developerPayload": "payload", "packageName": "com.some.market", "purchaseState": 0, "purchaseTime": 1520676644872, "productId": "card-1"}
The signature is like this:
hTFeQd25PZJ2DhGmXd0eO+C+oBeWsg983I4e5ztXtKAUrOIaNBaqAxHU3vW8acBs1I9fE5cxx/DI/sQGY4QSvpDnSm9aYz3do3joHPOXIVvXjSJfejxwzp9DKMUPd6LrgtxkaGevG+94NuKHFxpCdZlovEPXRJZyEznbASuYLqeW0KjP3jnvvw2O5iNlQRdh98h4Q18bSsaxq9zaRKExFLHkhNf/yO5m84kRB1G8
I'm searched for a method to do this but I don't know which method is true for me.
My verify code is this:
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(parameter);
var hash = new SHA1Managed();
bool dataOK =
rsa.VerifyData(hash.ComputeHash(Encoding.UTF8.GetBytes(json)), CryptoConfig.MapNameToOID("SHA1"), Encoding.UTF8.GetBytes(signature));
}
How truly convert signature to byte[] to verify? (with encoding or what???)
I'm searching a lot but more search more confuse.
Am I going the wrong way or using wrong method or ...?
Why should workflow be complicated?
Can anyone help me, please?
Thanks.
OK, I'll answer in order:
They use a hash algorithm to sign data and encrypt that hash with private key. I must decrypt signature with public key and compare hashes.
No, that's not correct. You should use a signature verification method, as you're currently doing. Seeing signature as encryption of a hash is incorrect; even the latest RSA standards go out of their way to explain this. For RSA the internal padding method is different. For ECDSA there is no direct encryption/decryption possible using the same scheme.
As I read on net first INTEGER is Modules and second INTEGER is Exponent
Yes, although it is spelled modulus, not modules. It is the public exponent, there is also a private exponent for the private key. Also without the caps.
How truly convert signature to byte[] to verify? (with encoding or what???)
Standard Base 64 is already mentioned in the comment section. Note that the key and signature size are not common (but that's OK in itself).
Why should workflow be complicated?
Well, somebody has to code it in the end and crypto is hard. But to make it easier for you: the entire ASN.1 structure is called a SubjectPublicKeyInfo structure; if you look on the internet you will find pre-made code to import from such a structure.
To finally verify the structure: make sure that you use the correct signature format (RSA-PKCS#1 v1.5 or RSA-PSS) and that you know exactly which binary data is fed to the signature generation function. For instance, the signature over the JSON could be in ASCII or UTF-8 or it could be in UTF-16 LE or BE.
Better ask the creator of the signature.
I'm trying to implement SHA256-RSA digital signatures and I'm confused with the terminology and implementation in C#.
AFAIK, "signing a file" is to generate the hash of a file, and then encrypt that hash. I've also heard the phrase "signing the hash". Is this the same thing? Or is this hashing a hash and then encrypting hash'?
Here's the code in question:
public void SignatureTest(byte[] data, X509Certificate2 cert)
{
var sha256 = new SHA256CryptoServiceProvider();
var rsa = (RSACryptoServiceProvider)cert.PrivateKey;
var hashOfData = sha256.ComputeHash(data);
var encryptedHash = rsa.Encrypt(hashOfData, false);
var encryptedHashOAEP = rsa.Encrypt(hashOfData, true);
var signedHash = rsa.SignHash(hashOfData, "SHA256");
//Shouldn't one of these be true?
var false1 = CompareAsBase64Str(encryptedHash, signedHash);
var false2 = CompareAsBase64Str(encryptedHashOAEP, signedHash);
//This is the one that actually matches
var true1 = CompareAsBase64Str(signedHash, rsa.SignData(data, sha256));
}
public bool CompareAsBase64Str(byte[] b1, byte[] b2)
{
return (Convert.ToBase64String(b1) == Convert.ToBase64String(b2));
}
Here's what MSDN says on RSACryptoServiceProvider:
SignHash() Computes the signature for the specified hash value by encrypting it with the private key.
Encrypt() Encrypts data with the RSA algorithm.
Shouldnt SignHash(hash) and Encrypt(hash) be the same?
You need to separate concerns, this will help you understand the terminology.
Any arbitrary blob of data can be hashed and/or encrypted in any combination.
Hash means: use a cryptographic algorithm to generate a value that is irreversible (that is, simply by knowing algorithm and hash you are unable to reconstitute original data) and consistent (that is, given the same data and algorithm, the value of the hash produced is always the same).
Encrypt means: use a cryptographic algorithm to encipher data (altogether or in blocks) with a given key (a key can be symmetric or asymmetric).
Sign means: Hash the data and Encrypt the hash with a given key. Then, given the pair (for asymmetric) or same (for symmetric) key, a consumer can validate that:
hash is matching, that means the data has not been altered in transit
hash did come from the source that at least has the pair key (for asymmetric) or same key (for symmetric)
The answer given by zaitsman is a good explanation of the topics related to your questions and I think should be the accepted answer, but just to help tie it back to your specific question of why encrypting the hash doesn't give you the same result as signing the hash (the rsa.SignHash(hashOfData, "SHA256") in your code):
Signing a hash doesn't just encrypt the hash data -- it also encrypts the name (or some identifier) of the hashing algorithm used to generate the hash along with it. Without that, the receiver wouldn't know what algorithm to use when computing their own hash (of the message being sent) to compare with the one they just decrypted in order to verify the authenticity of the message (which, of course, is the whole point).
When you encrypted the hash yourself (with rsa.Encrypt(hashOfData, false) and rsa.Encrypt(hashOfData, true)), you only encrypted the hash data and not the combination of hash data and algorithm identifier ("SHA256" in your code). In other words, you encrypted different data, so you got different (encrypted) results.
The reason the return value of that SignHash call does match the value returned by rsa.SignData(data, sha256) is that the latter method does the same thing, except it does the hashing and hash signing as one operation, so you don't have to compute the hash as a separate step if you don't need it for any purpose other than signing it.
From RSACryptoServiceProvider.SignData Method on MSDN:
Computes the hash value of the specified data and signs it.
Also see: Why does SignHash need to know what hash algorithm was used?
The key exchange method is: RSA-PKCS1-KeyEx
So I don't think the answers in other similar questions apply and I have no control over the certificate - it's valid and is supposed to work.
.NET Framework 3.5
The code:
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] bytestodecrypt = Convert.FromBase64String(SignatureValue);
byte[] plainbytes = rsa.Decrypt(bytestodecrypt, false);
Screenshot of the attributes of the rsa object:
While RSA can do both signatures and encryption, they can't be used interchangeably (except for at the raw/"no padding" version of the algorithm, which .NET does not support; and even then the keys are "backwards").
The signature blob is computed by "encrypting" with the private key. It gets "decrypted" by the public key, and then the underlying system compares it with the provided hash and hash algorithm. If they both match, it returns true; otherwise it returns false (or might throw an exception if it gets really confused).
So rather than rsa.Decrypt(signature, false) you want rsa.VerifyHash(hash, signature, hashAlgorithm, RSASignaturePadding.Pkcs1), which returns a bool.
The "bad key" exception is because you only have a public key (which is sufficient for verify) but called decrypt (which requires the private key).
I'm using C# and I'm trying to sign a string with a key produced from the PuTTY Key Generator. I'm not quite sure what I'm doing because I can't see how to import a key into the RsaEngine
I looked at the following:
AsymmetricKeyParameter key = new RsaKeyParameters(false, ???, ???);
RsaEngine rsa = new RsaEngine();
rsa.Init(true, key);
but it seems that RsaKeyParameter and AsymmetricKeyParameter take a modulus and a exponent which I don't know how to get from my already generated key? I could be using the wrong class though so if anyone has any pointers on this that would be excellent
I looked at: RSA signing and verification with C#,BouncyCastle and imported RSA key - Working Python example and non-working C# code sample inside but answer has a non existent function:
ConvertToRSAPrivateKey
You can first convert the PuttyGen keys into OpenSSH compatible keys and then convert this well documented format to C#.
The best I can come up with is this class of the SSH.NET library code.
In the mean time, please note that you are better off generating the key in the actual library that you want to use. Key formats, especially private key formats, are not always compatible.