Hash value generation using hmac/sha512 in java and c# - c#

in c#
public static string HashToString(string message, byte[] key)
{
byte[] b=new HMACSHA512(key).ComputeHash(Encoding.UTF8.GetBytes(message));
return Convert.ToBase64String(b);
}
client.DefaultRequestHeaders.Add("X-Hash", hash);
var encryptedContent = DataMotion.Security.Encrypt(key, Convert.FromBase64String(iv), serializedModel);
var request = client.PostAsync(ApiUrlTextBox.Text,encryptedContent,new JsonMediaTypeFormatter());
in java:
protected String hashToString(String serializedModel, byte[] key) {
String result = null;
Mac sha512_HMAC;
try {
sha512_HMAC = Mac.getInstance("HmacSHA512");
SecretKeySpec secretkey = new SecretKeySpec(key, "HmacSHA512");
sha512_HMAC.init(secretkey);
byte[] mac_data = sha512_HMAC.doFinal(serializedModel.getBytes("UTF-8"));
result = Base64.encodeBase64String(mac_data);
}catch(Exception e){
}
}
o/p: ye+AZPqaKrU14pui4U5gBCiAbegNvLVjzVdGK3rwG9QVzqKfIgyWBDTncORkNND3DA8jPba5xmC7B5OUwZEKlQ==
i have written hashtostring method in java based on c# code. is this currect? (output is different because every time process is dynamic in both cases.)

With different C# encoding
public static string SHA512_ComputeHash(string text, string secretKey)
{
var hash = new StringBuilder(); ;
byte[] secretkeyBytes = Encoding.UTF8.GetBytes(secretKey);
byte[] inputBytes = Encoding.UTF8.GetBytes(text);
using (var hmac = new HMACSHA512(secretkeyBytes))
{
byte[] hashValue = hmac.ComputeHash(inputBytes);
foreach (var theByte in hashValue)
{
hash.Append(theByte.ToString("x2"));
}
}
return hash.ToString();
}

Both java and C# code are giving same result(same hash code). You should check again.
Replace following line in java code at end
result = Base64.getEncoder().encodeToString(mac_data);

In c#
public static string HMACSHA512(this string Key, string TextToHash)
{
string HmacHashed = "";
if (string.IsNullOrEmpty(Key))
throw new ArgumentNullException("HMACSHA512: Key", "Parameter cannot be empty.");
if (string.IsNullOrEmpty(TextToHash))
throw new ArgumentNullException("HMACSHA512: TextToHash", "Parameter cannot be empty.");
if (Key.Length % 2 != 0 || Key.Trim().Length < 2)
{
throw new ArgumentNullException("HMACSHA512: Key", "Parameter cannot be odd or less than 2 characters.");
}
try
{
using (var HMACSHA512 = new HMACSHA512(Encoding.ASCII.GetBytes(Key)))
{
HmacHashed = BitConverter.ToString(HMACSHA512.ComputeHash(Encoding.ASCII.GetBytes(TextToHash))).Replace("-", string.Empty);
}
return HmacHashed;
}
catch (Exception ex)
{
throw new Exception("HMACSHA512: " + ex.Message);
}
}

Related

BouncyCastle (bc-sharp) decode DER formatted signature

How do I decode DER-formatted detached signature using BouncyCastle bc-sharp? For PEM-formatted signature I do it like this:
public static bool VerifyDetachedSignature(byte[] fileRawBytes, string sign)
{
try
{
var signatureFileRawBytes = Convert.FromBase64String(sign);
var cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), signatureFileRawBytes);
var signers = cms.GetSignerInfos();
var certificates = cms.GetCertificates("Collection");
var signerInfos = signers.GetSigners();
foreach (SignerInformation info in signerInfos)
{
var certList = new ArrayList(certificates.GetMatches(info.SignerID));
var cert = (X509Certificate)certList[0];
if (cert == null) throw new NullReferenceException();
var publicKey = cert.GetPublicKey();
info.Verify(publicKey);
}
return true;
}
catch (Exception exception)
{
return false;
}
}
On rare ocasions I need to verify DER-formatted signature. It appears I just need to covert string to byte array, like this:
public static bool VerifyDetachedSignature(byte[] fileRawBytes, string sign)
{
try
{
byte[] signatureFileRawBytes;
try
{
signatureFileRawBytes = Convert.FromBase64String(sign);
}
catch (FormatException)
{
signatureFileRawBytes = Encoding.ASCII.GetBytes(sign);
}
var cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), signatureFileRawBytes);
var signers = cms.GetSignerInfos();
var certificates = cms.GetCertificates("Collection");
var signerInfos = signers.GetSigners();
foreach (SignerInformation info in signerInfos)
{
var certList = new ArrayList(certificates.GetMatches(info.SignerID));
var cert = (X509Certificate)certList[0];
if (cert == null) throw new NullReferenceException();
var publicKey = cert.GetPublicKey();
info.Verify(publicKey);
}
return true;
}
catch (Exception exception)
{
return false;
}
}
In this case I get an exception on that line:
var cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), signatureFileRawBytes);
Org.BouncyCastle.Cms.CmsException
HResult=0x80131500
Message=IOException reading content.
Source=BouncyCastle
StackTrace:
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Asn1InputStream aIn)
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Stream input)
at Org.BouncyCastle.Cms.CmsSignedData..ctor(CmsProcessable signedContent, Byte[] sigBlock)
at backend.Helpers.CryptoHelper.VerifyDetachedSignature(Byte[] fileRawBytes, String sign) in C:\Projects\[...]\Helpers\CryptoHelper.cs:line 107
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
EndOfStreamException: DEF length 63 object truncated by 2
Any thoughts or suggestions on how to decode DER-signature?
It turned out the problem was not related to BouncyCastle. The problem was that I read binary data to a string variable and lose data. When i passed signature as byte array to VerifyDetachedSignature(byte[] fileRawBytes, byte[] sign) method it worked perfectly like this:
public static bool VerifyDetachedSignature(byte[] fileRawBytes, byte[] sign)
{
try
{
CmsSignedData cms;
try
{
cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), sign);
}
catch (CmsException)
{
var strSign = System.Text.Encoding.ASCII.GetString(sign);
var decodedSignRawBytes = Convert.FromBase64String(strSign);
cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), decodedSignRawBytes);
}
var signers = cms.GetSignerInfos();
var certificates = cms.GetCertificates("Collection");
var signerInfos = signers.GetSigners();
foreach (SignerInformation info in signerInfos)
{
var certList = new ArrayList(certificates.GetMatches(info.SignerID));
var cert = (X509Certificate)certList[0];
if (cert == null) throw new NullReferenceException();
var publicKey = cert.GetPublicKey();
info.Verify(publicKey);
}
return true;
}
catch (Exception)
{
return false;
}
}
Hope this helps someone to not make the same mistake.

Xamarin encrypting and decrypting in C # code Based on Android

Hello I have been exploring new tool that provides us Visual Studio 2015 that is Xamarin, I have already implemented an application on Android but I am currently translating it into C # for my application is platform, I have no idea how to perform encryption, I found the example below you'll stick but only encrypt and decrypt, also enclose the code that have already implemented in Java, I hope someone can support me because I get many errors.
loggedUser = null;
sharedPreferences = getSharedPreferences(AppConstants.PREFERENCES_FILE_NAME, MODE_PRIVATE);
if (sharedPreferences.getAll().isEmpty()) {
Intent loginActivityIntent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(loginActivityIntent);
finishAffinity();
}
String loggedUserUsername = sharedPreferences.getString(AppConstants.LOGIN_CREDENTIAL_USERNAME_KEY, "");
if (loggedUserUsername.isEmpty()) {
Intent loginActivityIntent = new Intent(MainActivity.this, LoginActivity.class);
loginActivityIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(loginActivityIntent);
finishAffinity();
} else {
File filesDirectory = new File(getFilesDir().getPath());
for (File file: filesDirectory.listFiles()) {
if (file.getName().contains(loggedUserUsername) && file.getName().contains(AppConstants.USER_INFO_FILE_SUFFIX)) {
String deviceId;
String deviceKey;
byte[] secretBytes;
byte[] ivBytes;
FileInputStream fileInputStream = null;
CipherInputStream cipherInputStream = null;
ObjectInputStream objectInputStream = null;
try {
deviceId = sharedPreferences.getString(AppConstants.LOGIN_CREDENTIAL_DEVICE_ID_KEY, "").replace("-", "");
deviceKey = sharedPreferences.getString(AppConstants.LOGIN_CREDENTIAL_DEVICE_KEY_KEY, "").replace("-", "");
secretBytes = deviceKey.substring(0, 16).getBytes();
ivBytes = deviceId.substring(deviceId.length() - 16, deviceId.length()).getBytes();
fileInputStream = openFileInput(file.getName());
final SecretKey secretKey = new SecretKeySpec(secretBytes, "AES");
final IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
cipherInputStream = new CipherInputStream(fileInputStream, cipher);
objectInputStream = new ObjectInputStream(cipherInputStream);
loggedUser = new User((String)objectInputStream.readObject());
} catch (ClassNotFoundException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IOException | JSONException e) {
if (AppConstants.DEBUG) Log.e(TAG, AppConstants.EXCEPTION_CAUGHT_MESSAGE + e.getMessage(), e);
loggedUser = null;
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
if (AppConstants.DEBUG) Log.e(TAG, AppConstants.EXCEPTION_CAUGHT_MESSAGE + e.getMessage(), e);
}
} else {
if (AppConstants.DEBUG) Log.e(TAG, "objectOutputStream:null");
}
if (cipherInputStream != null) {
try {
cipherInputStream.close();
} catch (IOException e) {
if (AppConstants.DEBUG) Log.e(TAG, AppConstants.EXCEPTION_CAUGHT_MESSAGE + e.getMessage(), e);
}
} else {
if (AppConstants.DEBUG) Log.e(TAG, "cipherOutputStream:null");
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
if (AppConstants.DEBUG) Log.e(TAG, AppConstants.EXCEPTION_CAUGHT_MESSAGE + e.getMessage(), e);
}
} else {
if (AppConstants.DEBUG) Log.e(TAG, "fileOutputStream:null");
}
}
break;
}
}
}
Here code C#
base.OnCreate(savedInstanceState);
loggedUser = null;
sharedPreferences = GetSharedPreferences(AppConstants.PREFERENCES_FILE_NAME, MODE_PRIVATE);
if (sharedPreferences.All.Count == 0)
{
Intent loginActivityIntent = new Intent(MainActivity.this, LoginActivity.Class);
loginActivityIntent.SetFlags(ActivityFlags.ClearTop);
StartActivity(loginActivityIntent);
FinishAffinity();
}
else
{
Java.IO.File filesDirectory = new Java.IO.File(FilesDir.Path);
foreach (Java.IO.File file in filesDirectory.ListFiles())
{
if (file.Name.Contains(loggedUser) && file.Name.Contains(AppConstants.USER_INFO_FILE_SUFFIX))
{
string deviceId;
string deviceKey;
byte[] secretBytes;
byte[] ivBytes;
FileInputStream fileInputStream = null;
CipherInputStream cipherInputStream = null;
ObjectInputStream objectInputStream = null;
try
{
deviceId = sharedPreferences.GetString(AppConstants.LOGIN_CREDENTIAL_DEVICE_ID_KEY, "").Replace("-","");
deviceId = sharedPreferences.GetString(AppConstants.LOGIN_CREDENTIAL_DEVICE_KEY_KEY, "").Replace("-", "");
secretBytes = System.Text.Encoding.UTF8.GetBytes(deviceId.Substring(0, 16));
ivBytes = System.Text.Encoding.UTF8.GetBytes(deviceId.Substring(deviceId.Length - 16, deviceId.Length));
Stream fileStream = OpenFileInput(file.Name);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(secretBytes);
Cipher cipher = Cipher.GetInstance("AES/CBC/PKCS5Padding");
cipher.Init(CipherMode.DecryptMode, secretKeySpec, ivSpec);
cipherInputStream = new CipherInputStream(fileStream,cipher);
objectInputStream = new ObjectInputStream(cipherInputStream); <- In this parte i have a error because not is possible to convert CipherInputStream to Stream
And this is the link with the example
Encryption and Decryption Support in .NET and Android
You could use a 3rd party library like PCL Crypto. It is also available as NuGet package.

can't get the same hmac_sha1 results for java and c# (winrt)

I'm trying to convert a java based code to c# as followed;
The original java code;
String str2 = "5f1fa09364a6ae7e35a090b434f182652ab8dd76:{\"expiration\": 1353759442.0991001, \"channel\": \"dreamhacksc2\", \"user_agent\": \".*"
Mac localMac = Mac.getInstance("HmacSHA1");
localMac.init(new SecretKeySpec("Wd75Yj9sS26Lmhve".getBytes(), localMac.getAlgorithm()));
String str3 = new BigInteger(1, localMac.doFinal(str2.getBytes())).toString(16);
Object[] arrayOfObject2 = new Object[2];
arrayOfObject2[0] = str3;
arrayOfObject2[1] = URLEncoder.encode(str2);
String str4 = String.format("%s:%s", arrayOfObject2);
And here is my WinRT based c# code
var token="5f1fa09364a6ae7e35a090b434f182652ab8dd76:{\"expiration\": 1353759442.0991001, \"channel\": \"dreamhacksc2\", \"user_agent\": \".*";
var encoding = new System.Text.UTF8Encoding();
var key = encoding.GetBytes("Wd75Yj9sS26Lmhve");
//var key = Convert.FromBase64String("Wd75Yj9sS26Lmhve");
var tokenData = encoding.GetBytes(token);
var result = HmacSha1(key, tokenData);
var hexString = new BigInteger(result).ToString("x");
var urlEncoded = System.Net.WebUtility.UrlEncode(token);
var combined = String.Format("{0}:{1}", hexString, urlEncoded);
and the hmacsha1 function as I'm running on WinRT;
public static byte[] HmacSha1(byte[] key, byte[] data)
{
var crypt = Windows.Security.Cryptography.Core.MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
var keyBuffer = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(key);
var cryptKey = crypt.CreateKey(keyBuffer);
var dataBuffer = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(data);
var signBuffer = Windows.Security.Cryptography.Core.CryptographicEngine.Sign(cryptKey, dataBuffer);
byte[] result;
Windows.Security.Cryptography.CryptographicBuffer.CopyToByteArray(signBuffer, out result);
return result;
}
So here's the corresponding outpus;
(JAVA) 92e893efe72a2f7df6ed409ce35819faba191a63:5f1fa09364a6ae7e35a090b434f182652ab8dd76%3A%7B%22expiration%22%3A+1353759442.0991001%2C+%22channel%22%3A+%22dreamhacksc2%22%2C+%22user_agent%22%3A+%22.*
(C#) 63b10e1d8e9f99cd7fba2ed46fe8e4a4a40222f5:5f1fa09364a6ae7e35a090b434f182652ab8dd76%3A%7B%22expiration%22%3A+1353759442.0991001%2C+%22channel%22%3A+%22dreamhacksc2%22%2C+%22user_agent%22%3A+%22.*
as shown above, the ouputs of HMAC_SHA1 from java and c# are not equal. Any ideas? Am I running encoding problems?
Just keep it simple and the code equal.
Java:
public static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (int i = 0; i < bytes.length; ++i) {
sb.append(String.format("%02x", bytes[i]));
}
return sb.toString();
}
public static void main(String[] args) {
String str2 = "5f1fa09364a6ae7e35a090b434f182652ab8dd76:{\"expiration\": 1353759442.0991001, \"channel\": \"dreamhacksc2\", \"user_agent\": \".*";
Mac localMac;
try {
localMac = Mac.getInstance("HmacSHA1");
localMac.init(new SecretKeySpec("Wd75Yj9sS26Lmhve"
.getBytes("UTF-8"), localMac.getAlgorithm()));
byte[] result = localMac.doFinal(str2.getBytes("UTF-8"));
String hexString = toHexString(result);
System.out.println(hexString);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Result:
f52202a4a4e4e86fd42eba7fcd999f8e1d0eb163
C#:
var token = "5f1fa09364a6ae7e35a090b434f182652ab8dd76:{\"expiration\": 1353759442.0991001, \"channel\": \"dreamhacksc2\", \"user_agent\": \".*";
var encoding = new System.Text.UTF8Encoding();
var privateKey = "Wd75Yj9sS26Lmhve";
HMACSHA1 hmac_sha1 = new HMACSHA1(encoding.GetBytes(privateKey));
hmac_sha1.Initialize();
byte[] result = hmac_sha1.ComputeHash(encoding.GetBytes(token));
string hexString = String.Join( "", result.Select( a => a.ToString("x2") ));
Console.WriteLine(hexString);
Result:
f52202a4a4e4e86fd42eba7fcd999f8e1d0eb163
Three tips:
When I tested your Java code, I received this value for str3: f52202a4a4e4e86fd42eba7fcd999f8e1d0eb163 That differs from both Java and C# result posted by you. (This online tool also calculates my result.)
Wikipedia contains an example, and it seems to be correct based on Java code and online calculator. In the first step, test your Java and C# code with the "The quick brown fox jumps over the lazy dog", "key", "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9" triplet.
It is not a good idea using BigInterger.toString(16) converting byte array to hex string, because when the byte array begins with one ore more zero digit (or hexit?), then the converted hex string will not contains the leading 0 characters.
You are confusing bytes with strings. The result of getBytes() depends on the default character-encoding, which may be different from system to system.

PgP Encryption and Decryption using BouncyCastle c#

I've seen a number of posts, followed a number of tutorials but none seems to work. Sometimes, they make reference to some classes which are not found. Can I be pointed to a place where I can get a simple tutorial showing how to encrypt and decrypt a file.
I'm very new to Pgp and any assistance is welcomed.
I know this question is years old but it is still #1 or #2 in Google for searches related to PGP Decryption using Bouncy Castle. Since it seems hard to find a complete, succinct example I wanted to share my working solution here for decrypting a PGP file. This is simply a modified version of the Bouncy Castle example included with their source files.
using System;
using System.IO;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Utilities.IO;
namespace PGPDecrypt
{
class Program
{
static void Main(string[] args)
{
DecryptFile(
#"path_to_encrypted_file.pgp",
#"path_to_secret_key.asc",
"your_password_here".ToCharArray(),
"output.txt"
);
}
private static void DecryptFile(
string inputFileName,
string keyFileName,
char[] passwd,
string defaultFileName)
{
using (Stream input = File.OpenRead(inputFileName),
keyIn = File.OpenRead(keyFileName))
{
DecryptFile(input, keyIn, passwd, defaultFileName);
}
}
private static void DecryptFile(
Stream inputStream,
Stream keyIn,
char[] passwd,
string defaultFileName)
{
inputStream = PgpUtilities.GetDecoderStream(inputStream);
try
{
PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
PgpEncryptedDataList enc;
PgpObject o = pgpF.NextPgpObject();
//
// the first object might be a PGP marker packet.
//
if (o is PgpEncryptedDataList)
{
enc = (PgpEncryptedDataList)o;
}
else
{
enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
}
//
// find the secret key
//
PgpPrivateKey sKey = null;
PgpPublicKeyEncryptedData pbe = null;
PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(
PgpUtilities.GetDecoderStream(keyIn));
foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
{
sKey = FindSecretKey(pgpSec, pked.KeyId, passwd);
if (sKey != null)
{
pbe = pked;
break;
}
}
if (sKey == null)
{
throw new ArgumentException("secret key for message not found.");
}
Stream clear = pbe.GetDataStream(sKey);
PgpObjectFactory plainFact = new PgpObjectFactory(clear);
PgpObject message = plainFact.NextPgpObject();
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());
message = pgpFact.NextPgpObject();
}
if (message is PgpLiteralData)
{
PgpLiteralData ld = (PgpLiteralData)message;
string outFileName = ld.FileName;
if (outFileName.Length == 0)
{
outFileName = defaultFileName;
}
Stream fOut = File.Create(outFileName);
Stream unc = ld.GetInputStream();
Streams.PipeAll(unc, fOut);
fOut.Close();
}
else if (message is PgpOnePassSignatureList)
{
throw new PgpException("encrypted message contains a signed message - not literal data.");
}
else
{
throw new PgpException("message is not a simple encrypted file - type unknown.");
}
if (pbe.IsIntegrityProtected())
{
if (!pbe.Verify())
{
Console.Error.WriteLine("message failed integrity check");
}
else
{
Console.Error.WriteLine("message integrity check passed");
}
}
else
{
Console.Error.WriteLine("no message integrity check");
}
}
catch (PgpException e)
{
Console.Error.WriteLine(e);
Exception underlyingException = e.InnerException;
if (underlyingException != null)
{
Console.Error.WriteLine(underlyingException.Message);
Console.Error.WriteLine(underlyingException.StackTrace);
}
}
}
private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyID, char[] pass)
{
PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyID);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.ExtractPrivateKey(pass);
}
}
}
I have used PgpCore package which is a wrapper around Portable.BouncyCastle.
It is very clean and simple to use. Multiple examples available here.
How's this:
PartialInputStream during Bouncycastle PGP decryption
Also, the zip contains examples here:
http://www.bouncycastle.org/csharp/
Hope this helps. If you're still stuck, post some more detail about what classes the compiler is complaining about and the community will take a look.
Now, in 2021, Nikhil's answer is probably best, since it abstracts out the need for working with BouncyCastle directly. Go give him an upvote.
If you want to work with BouncyCastle directly for some reason, I've got a modern implementation of Dan's answer, and the examples he's working from, that uses BouncyCastle directly in NET5. Take a look:
using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
Installed is Nuget Package Portable.BouncyCastle 1.8.10.
public class EncryptionService
{
public static void EncryptPGPFile(FileInfo inFile, FileInfo keyFile, FileInfo outFile, bool withIntegrityCheck = false, bool withArmor = false)
{
PgpPublicKeyRingBundle keyRing = null;
using (var keyStream = keyFile.OpenRead())
{
keyRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
}
var publicKey = keyRing.GetKeyRings()
.Cast<PgpPublicKeyRing>()
.FirstOrDefault()
?.GetPublicKeys()
.Cast<PgpPublicKey>()
.FirstOrDefault(x => x.IsEncryptionKey);
using var outFileStream = outFile.Open(FileMode.Create);
using var armoredStream = new ArmoredOutputStream(outFileStream);
Stream outStream = withArmor ? armoredStream : outFileStream;
byte[] compressedBytes;
var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
using (var byteStream = new MemoryStream())
{
// Annoyingly, this is necessary. The compressorStream needs to be closed before the byteStream is read from, otherwise
// data will be left in the buffer and not written to the byteStream. It would be nice if compressorStream exposed a "Flush"
// method. - AJS
using (var compressorStream = compressor.Open(byteStream))
{
PgpUtilities.WriteFileToLiteralData(compressorStream, PgpLiteralData.Binary, inFile);
}
compressedBytes = byteStream.ToArray();
};
var encrypter = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
encrypter.AddMethod(publicKey);
using var finalOutputStream = encrypter.Open(outStream, compressedBytes.Length);
finalOutputStream.Write(compressedBytes, 0, compressedBytes.Length);
}
public static void DecryptPGPFile(FileInfo inFile, FileInfo keyFile, string password, FileInfo outFile)
{
using var inputFile = inFile.OpenRead();
using var input = PgpUtilities.GetDecoderStream(inputFile);
var pgpFactory = new PgpObjectFactory(input);
var firstObject = pgpFactory.NextPgpObject();
if (firstObject is not PgpEncryptedDataList)
{
firstObject = pgpFactory.NextPgpObject();
}
PgpPrivateKey keyToUse = null;
PgpSecretKeyRingBundle keyRing = null;
using (var keyStream = keyFile.OpenRead())
{
keyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
}
var encryptedData = ((PgpEncryptedDataList)firstObject).GetEncryptedDataObjects()
.Cast<PgpPublicKeyEncryptedData>()
.FirstOrDefault(x =>
{
var key = keyRing.GetSecretKey(x.KeyId);
if (key != null)
{
keyToUse = key.ExtractPrivateKey(password.ToCharArray());
return true;
}
return false;
});
if (keyToUse == null)
{
throw new PgpException("Cannot find secret key for message.");
}
Stream clearText = encryptedData.GetDataStream(keyToUse);
PgpObject message = new PgpObjectFactory(clearText).NextPgpObject();
if (message is PgpCompressedData data)
{
message = new PgpObjectFactory(inputStream: data.GetDataStream()).NextPgpObject();
}
if (message is PgpLiteralData literalData)
{
using var outputFileStream = outFile.Open(FileMode.Create);
Streams.PipeAll(literalData.GetInputStream(), outputFileStream);
}
else
{
throw new PgpException("message is not encoded correctly.");
}
if (encryptedData.IsIntegrityProtected() && !encryptedData.Verify())
{
throw new Exception("message failed integrity check!");
}
}
}

Facebook Real-time Update: Validating X-Hub-Signature SHA1 signature in C#

When Facebook sends real-time updates, they include a X-Hub-Signature in the HTTP header. According to their documentation (http://developers.facebook.com/docs/api/realtime), they're using SHA1 and the application secret as the key. I tried to verify the signature like this:
public void MyAction() {
string signature = request.Headers["X-Hub-Signature"];
request.InputStream.Position = 0;
StreamReader reader = new StreamReader(request.InputStream);
string json = reader.ReadToEnd();
var hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(json), UTF8Encoding.UTF8.GetBytes("MySecret"));
var hmacBase64 = ToUrlBase64String(hmac);
bool isValid = signature.Split('=')[1] == hmacBase64;
}
private static byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody) {
using (var hmacAlgorithm = new System.Security.Cryptography.HMACSHA1(keyBody)) {
hmacAlgorithm.ComputeHash(dataToSign);
return hmacAlgorithm.Hash;
}
}
private static string ToUrlBase64String(byte[] Input) {
return Convert.ToBase64String(Input).Replace("=", String.Empty)
.Replace('+', '-')
.Replace('/', '_');
}
But I can't seem to get this to ever validate. Any thoughts on what I'm doing wrong?
Thanks in advance.
In case someone will need this information:
What Kelvin offered might work, but it seems very cumbersome.
All you need is instead of using the ToUrlBase64String function just use the ConvertToHexadecimal function.
See fully updated code below:
public void MyAction() {
string signature = request.Headers["X-Hub-Signature"];
request.InputStream.Position = 0;
StreamReader reader = new StreamReader(request.InputStream);
string json = reader.ReadToEnd();
var hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(json), UTF8Encoding.UTF8.GetBytes("MySecret"));
var hmacHex = ConvertToHexadecimal(hmac);
bool isValid = signature.Split('=')[1] == hmacHex ;
}
private static byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody) {
using (var hmacAlgorithm = new System.Security.Cryptography.HMACSHA1(keyBody)) {
return hmacAlgorithm.ComputeHash(dataToSign);
}
}
private static string ConvertToHexadecimal(IEnumerable<byte> bytes)
{
var builder = new StringBuilder();
foreach (var b in bytes)
{
builder.Append(b.ToString("x2"));
}
return builder.ToString();
}
The code below will resolve the problem for you:
public String hmacSha1(String keyString, byte[] in) throws GeneralSecurityException {
Mac hmac = Mac.getInstance("HmacSHA1");
int keySize = keyString.length();
byte[] keyBytes = new byte[keySize];
for (int i = 0; i < keyString.length(); i++) {
keyBytes[i] = (byte) keyString.charAt(i);
}
Key key = new SecretKeySpec(keyBytes, "HmacSHA1");
hmac.init(key);
hmac.update(in);
byte[] bb = hmac.doFinal();
StringBuilder v = new StringBuilder();
for (int i = 0; i < bb.length; i++) {
int ch = bb[i];
int d1 = (ch >> 4) & 0xf;
int d2 = (ch) & 0xf;
if (d1 < 10) {
v.append((char) ('0' + d1));
} else {
v.append((char) ('a' + d1 - 10));
}
if (d2 < 10) {
v.append((char) ('0' + d2));
} else {
v.append((char) ('a' + d2 - 10));
}
}
return v.toString();
}
public String callback(HttpServletRequest request) throws IOException {
InputStream in = request.getInputStream();
StringBuilder builder = new StringBuilder();
byte[] buffer = new byte[1024];
while (in.read(buffer) > 0) {
builder.append(new String(buffer));
}
String signature = request.getHeader("X-Hub-Signature");
try {
String signed = subscriptionService.hmacSha1("YOUR_SECRET", builder.toString().getBytes());
if (signature.startsWith("sha1=") && signature.substring(4).equals(signed)) {
// process the update
....
}
} catch (GeneralSecurityException ex) {
log.warn(ex.getMessage());
}
return null;
}

Categories