New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X509Certificate GetCertHash and GetCertHashString with SHA256 #16493

Closed
logiclink opened this Issue Feb 27, 2017 · 8 comments

Comments

Projects
None yet
5 participants
@logiclink

logiclink commented Feb 27, 2017

As the current implementation uses SHA1 for calculating X509 hashes and SHA1 can't be considered safe anymore the hash calculation should be updated to SHA256. E.G.:

X509Certificate cert = new X509Certificate("mycert.pxf");
byte[] b = cert. GetCertHash256()
string s = cert. GetCertHashString256()

Alternatively, a variable hashing algorithm as function parameter would be desirable for future hashing methods.
A use case is the ssl certificate validation on IoT devices to prevent man-in-the-middle attacks. Many IoTs connect to a web api via ssl and thus, should validate the server’s certificate. With limited compute power on the device the certificate’s thumbprint can be verified much cheaper than the whole chain of certificates.
For example, the validation of a server certificate can be done with python on a Raspberry:

import ssl
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes

thumbprint = 'e959fd5c80f76df7a593aae09686e604f74be8b0'
pem = ssl.get_server_certificate( (host, port) )
certx509 = x509.load_pem_x509_certificate(str.encode(pem), default_backend())
tp = certx509.fingerprint(hashes.SHA256())
tpHex = ''.join('{:02x}'.format(x) for x in tp)
if tpHex != thumbprint:
	print(‘Validation failed’)
@bartonjs

This comment has been minimized.

Member

bartonjs commented Feb 27, 2017

Until new API is added, this can be accomplished via

public static byte[] GetCertHash256(this X509Certificate2 cert)
{
    using (HashAlgorithm alg = SHA256.Create())
    {
        return alg.ComputeHash(cert.RawData);
    }
}
@bartonjs

This comment has been minimized.

Member

bartonjs commented Feb 27, 2017

Marking this as api-needs-work because:

  • Do we want more 0-argument thumprint methods, or should they take a HashAlgorithmName?
    • If they take a HashAlgorithmName, what do we want the "I don't know what this algorithm is" response to be?
  • Do we want both the string and the byte[] forms?
  • Does X509Certificate2Collection.Find (FindByThumbprint) need to work with a SHA-2-256 version?
    • If so, does that make it even more dangerous when SHA-3-256 is eventually added?

@bartonjs bartonjs modified the milestone: Future Feb 28, 2017

@morganbr

This comment has been minimized.

Contributor

morganbr commented Feb 28, 2017

@logiclink , while I do support this idea as general goodness, authenticating over only the thumbprint isn't recommended since it doesn't account for other important factors such as trusting the chain, expiration, and revocation status.

@logiclink

This comment has been minimized.

logiclink commented Mar 2, 2017

@bartonjs, many thanks for the hint using the raw data. Thus, two extension methods can solve the problem quite elegant:

public static class X509CertificateExtensions {
    public static byte[] GetCertHash(this X509Certificate certificate, HashAlgorithm alg) {
        return alg.ComputeHash(certificate.GetRawCertData());
    }
    public static string GetCertHashString(this X509Certificate certificate, HashAlgorithm alg) {
        return string.Concat(Array.ConvertAll(alg.ComputeHash(certificate.GetRawCertData()), x => x.ToString("X2")));
    }
}

// E.g.
using(HashAlgorithm sha256 = SHA256.Create()) {
	Console.WriteLine(certificate.GetCertHashString(sha256));
}
@logiclink

This comment has been minimized.

logiclink commented Mar 2, 2017

@morganbr I agree that comparing thumbprints in a regular browser and web server environment are far from being sufficient as authentication method. However, using thumbprints can reduce the risk of man-in-the-middle attacks like in rfc7469’s certificate pinning approach.
In my use case of IoT, the environment is different as server and clients are under control of a single operator. Thus, SSL certificates and key chains are known and static. There is no need for expiration or revocation of certificates as they can be replaced on the server and the clients simultaneously. Certificates are required for SSL encryption and the only risk is a man-in-the-middle attack on the communication between the IoT device and its web service. This can be minimized by comparing the thumbprint of the server’s certificate which needs only small compute power on the IoT device.
For authentication of IoT devices a login method with username and password can be used which returns a bearer token like JWT for subsequent web service calls.

@bartonjs bartonjs self-assigned this Jan 2, 2018

@bartonjs

This comment has been minimized.

Member

bartonjs commented Jan 2, 2018

API Proposal:

public partial class X509Certificate
{
        public virtual byte[] GetCertHash() { throw null; }
+       public virtual byte[] GetCertHash(HashAlgorithmName hashAlgorithm) { throw null; }
        public virtual string GetCertHashString() { throw null; }
+       public virtual string GetCertHashString(HashAlgorithmName hashAlgorithm) { throw null; }
}
@terrajobst

This comment has been minimized.

Member

terrajobst commented Jan 2, 2018

Looks good as proposed.

@karelz

This comment has been minimized.

Member

karelz commented Jan 2, 2018

FYI: The API review discussion was recorded - see https://youtu.be/dOpH6bUNbcw?t=2235 (2 min duration)

@karelz karelz modified the milestones: Future, 2.1.0 Jan 20, 2018

@bartonjs bartonjs removed their assignment Jan 29, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment