Generate SHA1 Hash in Portable Class Library

2019-02-16 07:29发布

问题:

I'm trying to build a portable class library that generates OAuth urls for other classes/applications to use. This class library using OAuth has to be a portable class library so it can work with different versions of a DropBox API I'm building.

Part of this class needs to generate an SHA1 hash to generate the oauth_signature with.

I'm aware that portable class library doesn't support System.Security.Cryptography, so is there anyway that this class can generate an SHA1 hash without that class?

回答1:

Mono provides a managed implementation of SHA1 for it's own mscorlib.dll (but it's not located in Mono.Security.dll like @CodeInChaos suggested).

It's open source, very well tested and meant to behave exactly like Microsoft implementation (e.g. it derives from SHA1, HashAlgorith... implements ICryptoTransform...) so it should be an easy drop-in replacement.



回答2:

I think the easiest way is to use the PCLCrypto nuget package. Then you can do:

private static string CalculateSha1Hash(string input)
{
        // step 1, calculate MD5 hash from input
        var hasher = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha1);
        byte[] inputBytes = Encoding.UTF8.GetBytes(input);
        byte[] hash = hasher.HashData(inputBytes);

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < hash.Length; i++)
        {
            sb.Append(hash[i].ToString("X2"));
        }
        return sb.ToString();
}


回答3:

Well I needed this too recently and I found much easier to take SHA1 implementation from HashLib : http://hashlib.codeplex.com/

Mono implementation have some far-going dependencies (localization of exceptions, etc.), while from HashLib you need only to copy few files without any changes in them:

Converters.cs
Hash.cs
HashBuffer.cs
HashCryptoNotBuildIn.cs
HashResult.cs
IHash.cs
SHA0.cs
SHA1.cs

55 KB of code total, so nothing too heavy.



回答4:

I have used this BouncyCastle Nuget package: https://www.nuget.org/packages/BouncyCastle-PCL/ and it works just fine for me (cross platforms Windows Store App, .Net Framework 4.5, Silverlight 5, Windows Phone 8, Xamarin.Android, Xamarin.iOS)

Use HMACSHA1 to generate signature like this:

public string GenerateSignature(string key, string signatureBase)
{
   var keyBytes = Encoding.UTF8.GetBytes(key);
   HMACSHA1 hashAlgorithm = new HMACSHA1(keyBytes);            
   byte[] dataBuffer = Encoding.UTF8.GetBytes(signatureBase);
   byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer);
   return Convert.ToBase64String(hashBytes);
}


回答5:

The SHA-1 Wikipedia article contains pseudocode that you could use as a guideline for your own implementation. But, as always with cryptographic functions, I strongly advise to use a tried and tested implementation.

Assuming you want a SHA-256 implementation, you can find one in BouncyCastle, which is available in source code form. The relevant class there is called Org.BouncyCastle.Crypto.Digests.Sha256Digest (here's its source).



回答6:

You might want to check out the new .NET Standard library:

https://docs.microsoft.com/en-us/dotnet/articles/standard/library

It is portable, and System.Security.Cryptography is included.

    /// <summary>
    /// Compute hash for string encoded as UTF8
    /// </summary>
    /// <param name="input">String to be hashed.</param>
    /// <returns>40-character hex string.</returns>
    public static string GetSha1(string input)
    {
        using (var sha1 = System.Security.Cryptography.SHA1.Create())
        {
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);
            byte[] hash = sha1.ComputeHash(inputBytes);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }
    }

You might also get some help (for creating a PCL project with .NET Standard Library) here:

https://xamarinhelp.com/dot-net-standard-pcl-xamarin-forms/



回答7:

I wanted sign OAuth also, and am looking at PCL Crypto - this test shows creation of a HmacSha1 hash, and compares the result to the standard .NET Framework way.

    [Test]
    public void CreateHash_VersusComputeHash_ReturnsEquivalent()
    {
        // USING TRADITIONAL .NET:
        var key = new byte[32];
        var contentBytes = Encoding.UTF8.GetBytes("some kind of content to hash");
        new RNGCryptoServiceProvider().GetBytes(key);

        var alg = new HMACSHA1(key); // Bouncy castle usage does not differ from this
        var result = alg.ComputeHash(contentBytes);




        // USING PCL CRYPTO:
        var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1);

        byte[] mac;
        using (var hasher = algorithm.CreateHash(key))
        {
            hasher.Append(contentBytes);
            mac = hasher.GetValueAndReset();
        }




        // Assert results:
        Assert.AreEqual(result.Length, mac.Length);

        for (var i = 0; i < result.Length; i++)
        {
            Assert.AreEqual(result[i], mac[i]);
        }
    }


回答8:

This worked for me when I had to achieve the same outcome. You can do this with SHA512 and others too.

using System.Security.Cryptography;

public static string HashSHA1(this string value)
{
    using (var sha = SHA1.Create())
    {
       return Convert.ToBase64String(sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(value)));
    }
}

Code cited from: https://xamarinhelp.com/cryptography-in-xamarin-forms/



回答9:

Here is an example using BouncyCastle

    public static string ComputeSha1(string data)
    {
        var sha1Digest = new Org.BouncyCastle.Crypto.Digests.Sha1Digest();
        var hash = new byte[sha1Digest.GetDigestSize()];

        var dataBytes = Encoding.UTF8.GetBytes(data);
        foreach (var b in dataBytes)
        {
            sha1Digest.Update(b);
        }
        sha1Digest.DoFinal(hash, 0);

        return string.Join("", hash.Select(b => b.ToString("x2")).ToArray());
    }