how to convert c# code to perl or openssl cli? - c#

I have a c# code that convert a string to md5 and then base64 encode it.
I would like to do the same thing with a perl script or linux cli, openssl.
i tried using openssl cli, but i get a different result from the c# code.
can anyone explain and show me how to convert the c# code to linux cli or perl script?
Thanks.
public class Program {
public static void Main() {
var myString = "7512";
var o = CalculateMD5Hash(myString);
Console.WriteLine("Generated String is: {0}", o);
}
public static string CalculateMD5Hash(string input) {
HashAlgorithm ha = new MD5CryptoServiceProvider();
string prefix = "MD5:";
string password = "";
UnicodeEncoding enc = new UnicodeEncoding();
password = Convert.ToBase64String(ha.ComputeHash(enc.GetBytes(input)));
return prefix+password;
}
}
# Linux openssl cli
echo -ne '7512' | openssl dgst -md5 -binary | openssl base64
When i run the c# code with the string: 7512, i get:
cA5YjDeU2fOJwwnVFPCuAw==
But when i am using the openssl cli command, i get:
FhxcWtUfzIhBV4kFEbPIsA==

Well, got an answer in perlmonks site.
https://perlmonks.org/index.pl?node_id=1227872
UnicodeEncoding encodes to UTF-16LE. So in Perl, you'll have to use the Encode module for that, and then either use md5_base64 from Digest::MD5, which removes the padding at the end of the string (==), or, if you want the padding, you'll have to use MIME::Base64 separately:
use warnings;
use strict;
use Encode qw/encode/;
use Digest::MD5 qw/md5/;
use MIME::Base64 qw/encode_base64/;
my $string = "7512";
my $md5b64 = encode_base64( md5( encode( 'UTF-16LE', $string,
Encode::FB_CROAK|Encode::LEAVE_SRC ) ), "" );
print "Generated String is: MD5:$md5b64\n";
my $expect = "cA5YjDeU2fOJwwnVFPCuAw==";
print $md5b64 eq $expect ? "Matches!\n" : "Doesn't match!\n";
__END__

Related

C# - Looking for Encryption/Decryption Method

I've written a C# piece that encrypts/decrypts a string using RtlEncryptMemory/RtlDecryptMemory. This string is then saved in a config file, it all works well but the problem is that once I logoff/logon, I can no longer decrypt the string. I am using the RTL_ENCRYPT_OPTION_SAME_LOGON option which means the internal mechanism uses something from the Windows session in order to perform the decryption. I am looking for a solution that works in the same manner but is tied to the network user (or token, etc...). Is Windows providing something already?
My goal is to be able to decrypt the string from anywhere as long as the process is running under the same user (network credentials). I also do not want to have the user type in a password or use an internal value as that could be compromised. Ideally it would be just like the RTL functions but provide an RTL_ENCRYPT_OPTION_SAME_USER option.
You want to use the DataProtection API
Here is a simple implementation that adds Encrypt and Decrypt string extensions...
public static class StringExtensions
{
public static string Encrypt(this string s)
{
if (String.IsNullOrEmpty(s))
{
return s;
}
else
{
var encoding = new UTF8Encoding();
byte[] plain = encoding.GetBytes(s);
byte[] secret = ProtectedData.Protect(plain, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(secret);
}
}
public static string Decrypt(this string s)
{
if (String.IsNullOrEmpty(s))
{
return s;
}
else
{
byte[] secret = Convert.FromBase64String(s);
byte[] plain = ProtectedData.Unprotect(secret, null, DataProtectionScope.CurrentUser);
var encoding = new UTF8Encoding();
return encoding.GetString(plain);
}
}
}
Here is an example...
class Program
{
static void Main(string[] args)
{
string password = "Monkey123";
string encrypted = password.Encrypt();
Console.WriteLine($"Encrypted password = '{encrypted}'");
string decrypted = encrypted.Decrypt();
Console.WriteLine($"Decrypted password = '{decrypted}'");
}
}
Which produces this output...
Encrypted password = 'AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA/6wDgM21DkStrNJQ35QDiwAAAAACAAAAAAAQZgAAAAEAACAAAAAPr3/aqafbt/RRoPVe75b+PFBhE6h9MLcQ2Ivsd3adOwAAAAAOgAAAAAIAACAAAABYxqEdzotL+7qXpWnbbpPRkfWZF6oh/meFsXzFtLPnrBAAAAB59VGbboP4Tye1N3dB7E3jQAAAAMQn8cAlnTDe1mwDEJriADizdT2Qr0DtPgpMje+rbjdkVpL+cKiEQs4om4i1hlLPgPn5MG5oVWFFnxU0d4c9TFg='
Decrypted password = 'Monkey123'
Notes:
Only the currently logged in user can decrypt the data encrypted with this code. This works across the network as long as the current user has a roaming profile.
Alternatively the scope can be local machine in which case only users logged in to the same machine can decrypt the data.
This is .NET Core 3.1 code and works only on Windows machines
Using statements...
using System;
using System.Security.Cryptography;
using System.Text;
You should not be using RtlEncryptMemory if you want to store the string, it is meant to only keep strings secure inside the running applications memory, it therefore can be stored/serialized and decrypted.
Have a look at DPAPI password encryption I think it should meet your needs.
I have a Nuget package you might like:
DataJuggler.Net.Cryptography .Net Framework
DataJuggler.Core.Cryptography Dot Net Core
Pretty simple to work with, here is a live demo:
https://blazorcrypto.datajuggler.com/
Source code and video link is available above also.
Usage:
Encryption:
// get the encryptedText
encryptedResult = CryptographyHelper.EncryptString(textToEncrypt, keyCode);
Decryption:
// get thedecryptedText
decryptedResult = CryptographyHelper.DecryptString(textToDecrypt, keyCode);
It also includes password hashing.
Let me know if you think it is worth the price of free.

Signing string in php using certificate, verifying in C#

I am in need of assistance with signing a string in PHP. We are building a web application which is able to alter profile data on an external website. We want to achieve this without requiring end users to enter our password on our site. The external website therefore created an API which allows us to edit the profile as long we send them email address of the profile signed by a certificate. This allows them to check whether the request came from a trusted source (us).
We tried both native PHP functions and PHPSeclib to sign an email address using a certificate:
$rsa = new RSA();
$rsa->setHash("sha1");
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$originalkey = file_get_contents('key.ppk');
$rsa->loadKey($originalkey);
echo bin2hex($rsa->sign("test#email.nl"));
// Using native PHP methods
$email = 'test#email.nl';
$signature = '';
$private_key = openssl_pkey_get_private(file_get_contents("key.ppk"));
var_dump(openssl_sign($email, $signature, $private_key, "sha1"));
echo bin2hex($signature);
The signatures are fine because PHP is able to verify the posted signatures. The webservice of the external website does not accept our signatures however. They only allow a signature length of 40 characters whilst a hexidecimal dump of our signature exceeds 1000 characters.
They sent us this C# example to sign a string but we do not have enough C# knowledge to see why the output differs.
string Sign(string username, string thumbprint, string hasher = "SHA1")
{
var store = new
System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine);
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.OpenExistingOnly |
System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
try
{
foreach (System.Security.Cryptography.X509Certificates.X509Certificate2 certificate in store.Certificates.Find(System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint, thumbprint, false))
{
var privateKey = certificate.PrivateKey as System.Security.Cryptography.RSACryptoServiceProvider;
if (privateKey != null)
{
var bytes = privateKey.SignData(Encoding.UTF8.GetBytes(username),
System.Security.Cryptography.HashAlgorithm.Create(hasher));
return string.Join("", bytes.Select(b => b.ToString("x2")));
} else throw new ArgumentException("no private key");
}
}
finally
{
store.Close();
}
return null;
}
Is there anyone who can point us to the right direction?
Thanks in advance!

Apparently (!) inconsistent signing between .NET and Mono; Mono signing is not idempotent

Google Cloud Storage provides Java, C# code samples for generating signed URLs:
https://cloud.google.com/storage/docs/access-control?hl=en#signing-code-csharp
I'm using the code sample. With the same service account|key, bucket and object, the Java code and the C# code (on Windows) work. When I try the C# code on Mono/Linux, it does not work. The error is:
Code: SignatureDoesNotMatch
Message: The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
Injecting some debugging code corroborates this error.
Here's the augmented method that does the signing:
private String signString(String stringToSign) {
if (key == null) throw new Exception("Certificate not initialized");
CspParameters cp = new CspParameters(
24,
"Microsoft Enhanced RSA and AES Cryptographic Provider",
((RSACryptoServiceProvider)key.PrivateKey).CspKeyContainerInfo.KeyContainerName
);
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(cp);
byte[] buffer = Encoding.UTF8.GetBytes(stringToSign);
byte[] rawSignature = provider.SignData(buffer, CryptoConfig.MapNameToOID("SHA256"));
Console.WriteLine ("signature == ");
Console.WriteLine (BitConverter.ToString(rawSignature).Replace("-", string.Empty));
return Convert.ToBase64String(rawSignature);
}
I expect (perhaps incorrectly that) repeated calls to signString with the same string value, would return the same rawSignature and result. On Java and Windows, this is true. On Mono, the value changes
String testString="helloworld";
Console.WriteLine("Signing '" + testString + "' == " + this.signString(testString));
Console.WriteLine("Signing '" + testString + "' == " + this.signString(testString));
Console.WriteLine("Signing '" + testString + "' == " + this.signString(testString));
returns, abbreviated results:
signature ==
4415768E8E2FB862...
Signing 'helloworld' == RBV2jo4v...
signature ==
95E589C2F8DAD7ED...
Signing 'helloworld' == leWJwvja...
signature ==
0589E4454FE4FB3A...
Signing 'helloworld' == BYnkRU/k...
With Java, a similar test using the Google sample returns:
rawSignature ==
3E56F09EE9CF7D98...
Signing 'helloworld' == PlbwnunP...
rawSignature ==
3E56F09EE9CF7D98...
Signing 'helloworld' == PlbwnunP...
rawSignature ==
3E56F09EE9CF7D98...
Signing 'helloworld' == PlbwnunP...
What am I doing wrong?
I've managed to get consistent results by not using CspParameters at all, but using the PrivateKey property of X509Certificate2. My current "portable" implementation unfortunately requires a cast which makes me nervous, but it does appear to give the same results on Windows and Linux (under Mono 4.0.2), and those are the same results as the original sample code. Here's a short test app which works under Mono:
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
public class Test
{
static void Main()
{
var key = new X509Certificate2("key.p12", "notasecret");
byte[] buffer = Encoding.UTF8.GetBytes("test string");
// This is the slightly dodgy bit...
var rsa = (RSACryptoServiceProvider) key.PrivateKey;
byte[] signature = rsa.SignData(buffer, "SHA256");
Console.WriteLine(Convert.ToBase64String(signature));
}
}
As noted in comments, this doesn't work under .NET, for unknown reasons :(
Now I haven't tried using this to make any Cloud Storage requests yet, but I expect it would work.
On more modern platforms, you can use the fact that RSA has a SignData method, and use the GetPrivateRSAKey() extension method to get an RSA instance:
var rsa = key.GetRSAPrivateKey();
var signature = rsa.SignData(
buffer, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
You may be able to use this on Mono too depending on which version you target.

Why is the first line missing after encrypting and decrypting when calling GnuPG from C#?

I am encrypting a file using GnuPG in Visual Studio 2012 using C#.
When I decrypt this encrypted file it doesn't show row headers in the file.
Below is what I have written for the encryption.
string gpgOptions = "--homedir "C:\GnuPG" --batch --yes --encrypt --armor --recipient ankit.gupta1#nirvana-sol.com --default-key ankit.gupta1#nirvana-sol.com --passphrase-fd 0 --no-verbose";
string inputText = "Name, Age
Dave, 19
Ryan, 21";
ProcessStartInfo pInfo = new ProcessStartInfo(gpgExecutable, gpgOptions);
pInfo.CreateNoWindow = true;
pInfo.UseShellExecute = false;
pInfo.RedirectStandardInput = true;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
_processObject = Process.Start(pInfo);
_processObject.StandardInput.Write(inputText);
I decrypt it using stdin with the below command:
gpg --output my.csv --decrypt A.gpg
my.csv is:
"Dave, 19
Ryan, 21";
I want it to be decrypted with the headers. What am I missing here?
You're telling gpg to read the passphrase from stdin using --passphrase-fd 0. Because of this, GnuPG will use everything up to the first newline as password.
This works anyway, as encryption does not require any passphrase. Try this (example is for unix systems, not Windows, but probably wouldn't look much different over there) to demonstrate the problem:
$ cat <<EOT | gpg -e --passphrase-fd 0 --recipient email#jenserat.de | gpg -d
> foo
> bar
> EOT
Reading passphrase from file descriptor 0
You need a passphrase to unlock the secret key for
user: "Jens Erat <email#jenserat.de>"
2048-bit RSA key, ID 3A5E68F7, created 2010-12-12 (main key ID D745722B)
gpg: encrypted with 2048-bit RSA key, ID 3A5E68F7, created 2010-12-12
"Jens Erat <email#jenserat.de>"
bar
Solution: Remove --passphrase-fd 0 from the parameters.

How to Decrypt data with C# from data which encrypt with PHP

I try to decrypt data which is encrypted with PHP from local money transfer service.
There is PHP example as following:
<?php
require_once('phpseclib/Crypt/AES.php');
define('API_PASSKEY', 'abcdefghijklmnop');
if($_SERVER['REMOTE_ADDR'] == '203.146.127.115' && isset($_GET['request']))
{
$aes = new Crypt_AES();
$aes->setKey(API_PASSKEY);
$_GET['request'] = base64_decode(strtr($_GET['request'], '-_,', '+/='));
$_GET['request'] = $aes->decrypt($_GET['request']);
if($_GET['request'] != false)
{
parse_str($_GET['request'],$request);
$request['Ref1'] = base64_decode($request['Ref1']);
echo 'SUCCEED';
}
else
{
echo 'ERROR|INVALID_PASSKEY';
}
}
else
{
echo 'ERROR|ACCESS_DENIED';
}
?>
However, I want to do decrypt with C#.
I also set up simple PHP encrypt text for testing but cannot decrypt with C# either.
<?php
include('phpseclib/Crypt/AES.php');
$aes = new Crypt_AES();
$aes->setKey('abcdefghijklmnop');
$plaintext = 'Hello';
$cryptoText = $aes->encrypt($plaintext) ;
$cryptoText = base64_encode( $cryptoText);
echo $cryptoText . "<br/>";
echo $aes->decrypt(base64_decode($cryptoText));
?>
I looking forward to get your good suggestion.
phpseclib uses CBC by default with PKCS#7 padding enabled and 128-bit keys if that helps.

Categories