Difference in SHA hashes between ruby and C# - c#

I'm developing an application, that makes use of some REST web services.
It's technical documentation says that I should pass SHA256 hash of some string in the request.
In an example request (in the documentation) a string:
hn-Rw2ZHYwllUYkklL5Zo_7lWJVkrbShZPb5CD1expires=1893013926label[0]=any/somestatistics=1d,2d,7d,28d,30d,31d,lifetimestatus=upl,livetitle=a
After executing:
digest = Digest::SHA256.digest(string_to_sign)
signature = Base64::encode64(digest).chomp.gsub(/=+$/, '')
results in a hash:
YRYuN2zO+VvxISNp/vKQM5Cl6Dpzoin7mNES0IZJ06U
This example is in ruby, as the documentation is for ruby developers.
I'm developing my application in C# and for the exactly same string, when I execute:
byte[] rawHash = sha256.ComputeHash(rawRequest, 0, rawRequest.Length);
string friendlyHash = Convert.ToBase64String(rawHash);
and remove the trailing "=" signs, I get:
Vw8pl/KxnjcEbyHtfNiMikXZdIunysFF2Ujsow8hyiw
and therefore, the application fails to execute resulting in an signature mismatch error.
I've tried changing the encoding while converting the string to a byte array preceding the hashing and nothing changed.
Any ideas?

Based on the document here, you are missing a - (that is a dash) in your string. Seems that Acrobat helpfully removes it in a copy paste from the document...
Here is some code that I splatted together that gets the same value as the example (well it would if you trimmed the final =)
string s = "hn-Rw2ZH-YwllUYkklL5Zo_7lWJVkrbShZPb5CD1expires=1893013926label[0]=any/somestatistics=1d,2d,7d,28d,30d,31d,lifetimestatus=upl,livetitle=a";
SHA256Managed sh = new SHA256Managed();
byte[] request = System.Text.UTF8Encoding.UTF8.GetBytes(s);
sh.Initialize();
byte[] b4bbuff = sh.ComputeHash(request, 0, request.Length);
string b64 = Convert.ToBase64String(b4bbuff);

Related

C# SHA256 RSA Signing. How to take a string from byte[] sign?

I have to use some API, which service who has my certificate. They propably wants my sign to verify if it is the same with certificate on their server. Im not sure of that, but. In documentation it is instruction:
sign_base =
<?xml version="1.0" encoding="UTF-8"?> <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
//big XML, but i can't paste it here, because of NDA i signed (with pen ;-))
</ds:Reference> </ds:SignedInfo>
rsa_sha256(sign_base) =
113c7ed1e66e1ef77c0857d418928bdf1d3bdf27b8d06c7...40d1c0a259733ad0b1b2
// its long hash so i made "..." it is 516 characters there.
base64(rsa_sha256) = ETx+0eZuHvd8CFfUGJKL3x073ye40Gx7mB0.... tCxsg==
//it is a base64 of rsa_256 hash above.
Inspired by SoF questions i tried to use RSACryptoServiceProvider provider = new RSACryptoServiceProvider(); and then var signed = provider.SignData(sign_base, CryptoConfig.CreateFromName("SHA256")); but result gives me only bytes[]. When i decoded bytes[] to UTF-8 string result was unreadable "?????????..???" I can compare that with other signs, and do all stuff allowed by RSACryptoServiceProvider() but my question is:
They want to send this hash in request as parameter, so it is declared as base64 string. I have to send only base64 formatted message, but i have to count it first.
How to get string of this sign? Maybe there is mistake in my choices, and i am doing it wrong? If you give me help with different language than C# it will be helpfull too.
If I understand well and provided that your signature is correct, once you have it as byte[], you should be able to use Convert.ToBase64String(byte[] data) using System.Convert.ToBase64String.
Unlike what you did, you should be able to see a String looking like the example.

OtpSharp not working with google authenticator

I've been trying to use OtpSharp along with Google Authenticator in an application I'm developing. However, I don't understand why the code produced by OtpSharp does not match that of Google Authenticator. I've even tried to correct the time input to OtpSharp according to my local OS without any luck. On another note, the pyotp library from python works just fine without any special effort.
Here is the code I'm using:
var bSharedKey = System.Text.Encoding.Unicode.GetBytes("TESTTESTTESTTEST");
//var correction = new TimeCorrection(DateTime.UtcNow.ToLocalTime());
//var totp = new Totp(bSharedKey, timeCorrection: correction);
var totp = new Totp(bSharedKey);
var realOtp = totp.ComputeTotp();
long timestep = 0;
var OTPmatch = totp.VerifyTotp(passwords[1], out timestep);
The problem was that instead of providing an arbitrary unicode key to the pyotp library (as well as to Google Authenticator), a Base32 string was needed as the input, which I assume was later decoded to a byte array and used by the library.
So I provided OtpSharp with the byte string representation of an arbitrary unicode string and used an online website to decode the unicode string to a base32 string, and used the base32 string in Google Authenticator.
To put it simply, Otpsharp requires a byte array to initialize a totp object, while pyotp needs you to provide it with a base32 string.
I used the following solution:
First
Choose a secret key.
e.g: 32 Chars
private const string SecretKey = "hfBhdVsbAWXmkdWrcnwezQqVLubqeRdq";
Second
Use an approach to generate QR code with your secret.
e.g:
https://stefansundin.github.io/2fa-qr/
How to generate a QR Code for Google Authenticator that correctly shows Issuer displayed above the OTP?
Point Key: You have to convert your secret key to Base32 to generate QR Code.
You can use https://emn178.github.io/online-tools/base32_encode.html to convert a string to base32 online
Third
Install OtpSharp nuget package.
Validate entered token as follows:
XXX: Valid Period In Seconds
private static bool Validate(string token)
{
var totp = new Totp(Encoding.UTF8.GetBytes(SecretKey));
return totp.VerifyTotp(DateTime.UtcNow.AddSeconds(XXX), token, out _, new VerificationWindow(2, 2));
}

Encrypt and decrypt parameter passed in on query string

I'm using (or attempting to!) the code for encrypting/decrypting a string found in this post Encrypting & Decrypting a String in C#.
If I call the functions directly, i.e.
StringHelper sHelp = new StringHelper();
var encryptMe = sHelp.EncryptString("comahony#centlaw.com", "myPassphrase");
Returns /sx3sL4DE7sM2klGKN3V+CQKdP02ZxbVxANjDh2yfGo= which is perfect
and if I then call
var decryptMe = sHelp.DecryptString(encryptMe, "myPassphrase");
Returns comahony#centlaw.com which again is what I'm after.
But if I pass the encrypted string on parameter on the querystring. i.e.
http://localhost:12345/sso?c=/sx3sL4DE7sM2klGKN3V+CQKdP02ZxbVxANjDh2yfGo=
and call the decryption function using:
var decryptMe = sHelp.DecryptString(Request.QueryString["c"].ToString(), "myPassphrase");
I'm getting the error of "Invalid length for a Base-64 char array or string."
From digging around on the net it appears to be something to do with the parameter needing to be URLEncoded but try as I might I just can't get past this error.
Could something shed some light please?
Thanks,
C

Can Python encode a string to match ASP.NET membership provider's EncodePassword

I'm working on a Python script to create hashed strings from an existing system similar to that of ASP.NET's MembershipProvider. Using Python, is there a way to take a hexadecimal string and convert it back to a binary and then do a base64 encoding, somehow treating the original string as Unicode. Let's try some code. I'm looking to re-encode a hashed password so that the hashes would be equal in Python and ASP.NET/C#:
import base64
import sha
import binascii
def EncodePassword(password):
# strings are currently stored as hex
hex_hashed_password = sha.sha(password).hexdigest()
# attempt to convert hex to base64
bin_hashed_password = binascii.unhexlify(hex_hashed_password)
return base64.standard_b64encode(bin_hashed_password)
print EncodePassword("password")
# W6ph5Mm5Pz8GgiULbPgzG37mj9g=
The ASP.NET MembershipProvider users this method to encode:
static string EncodePassword(string pass)
{
byte[] bytes = Encoding.Unicode.GetBytes(pass);
//bytes = Encoding.ASCII.GetBytes(pass);
byte[] inArray = null;
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
inArray = algorithm.ComputeHash(bytes);
return Convert.ToBase64String(inArray);
}
string s = EncodePassword("password");
// 6Pl/upEE0epQR5SObftn+s2fW3M=
That doesn't match. But, when I run it with the password encoded with ASCII encoding, it matches, so the Unicode part of the .NET method is what's the difference.
W6ph5Mm5Pz8GgiULbPgzG37mj9g=
Is there a way in the python script to get an output to match the default .NET version?
This is the trick:
Encoding.Unicode
“Unicode” encoding is confusing Microsoft-speak for UTF-16LE (specifically, without any BOM). Encode the string to that before hashing and you get the right answer:
>>> import hashlib
>>> p= u'password'
>>> hashlib.sha1(p.encode('utf-16le')).digest().encode('base64')
'6Pl/upEE0epQR5SObftn+s2fW3M=\n'

Digitally sign in PHP using private key, verify in C#

I am working on a feature that needs me to digitally sign a short string in PHP, and verify the string's signature in C#.
I would really like to use openssl_sign in PHP, because of its simplicity, but all the information I can find on Google indicates that this will not work.
There are some external libraries that claim to do this well, however as this is a hobby project I would rather not purchase such a library.
So what are the alternatives here? Full interoperability between C# and PHP is required. Libraries besides OpenSSL can be used.
I've done something very similar using Bouncy Castle Crypto APIs. It appears PHP openssl_sign uses SHA1 by default. If you are using anything other than the default you'll need to change the algorithm parameter for GetSigner.
string base64pubkey = "<!-- BASE64 representation of your pubkey from open ssl -->";
RsaKeyParameters pubKey = PublicKeyFactory.CreateKey(Convert.FromBase64String(base64pubkey)) as RsaKeyParameters;
byte[] signature = Convert.FromBase64String("<!-- BASE64 representation of your sig -->");
byte[] message = Encoding.ASCII.GetBytes("Something that has been signed");
ISigner sig = SignerUtilities.GetSigner("SHA1WithRSAEncryption");
sig.Init(false, pubKey);
sig.BlockUpdate(message, 0, message.Length);
if (sig.VerifySignature(signature))
{
Console.WriteLine("all good!");
}
You may use to check the digital signature smth like this:
string publicKey = "some key";
// Verifying Step 1: Create the digital signature algorithm object
DSACryptoServiceProvider verifier = new DSACryptoServiceProvider();
// Verifying Step 2: Import the signature and public key.
verifier.FromXmlString(publicKey);
// Verifying Step 3: Store the data to be verified in a byte array
FileStream file = new FileStream(args[0], FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(file2);
byte[] data = reader.ReadBytes((int)file2.Length);
// Verifying Step 4: Call the VerifyData method
if (verifier.VerifyData(data, signature))
Console.WriteLine("Signature verified");
else
Console.WriteLine("Signature NOT verified");
reader.Close();
file.Close();
Is there a reason you need something as complex as SSL signing? Can't you just use a simple one-way hash like MD5/SHA-1 on the string? If all you're looking for is verification that the string wasn't tampered with, that should be sufficient.
So looking at this - this guy appears to have asymmetric signing and encrypting working between PHP and C#. Signing should not be a problem, SHA* and MD* are standard, and so it's very very unlikely that is going to not be compatible (although you should be looking at SHA256 as MD* and SHA1 are deprecated due to vulnerabilities)
We're missing some context as to why you need to sign it. You may not need to.
The important question is: what guarantees do you need from your data?
If all you need to do is verify the integrity of the data, a hash will do the job. If you need to verify where it's coming from, you need to sign it. If you need both, hash it, concatenate the payload with the hash, and sign the whole thing.
Regarding cross-platform libraries... you really should need to worry about it. A SHA1 is a SHA1 is a SHA1, no matter which library generated it. Same thing with generating and verifying digital signatures. Use what's easiest in PHP and use what's easiest in C#. If they're both set up correctly you shouldn't need to worry about it.

Categories

Resources