I can't get the signature verification working, like it's described here. I'm using BouncyCastle.NetCore 1.8.1.3 and the project is a .NETCoreApp 1.0.
I'm developing on macOS 10.12.1, running dotnet core 1.0.4 and my server is running Ubuntu 16.04.2-x64 running the release version, build as netcore1.0 and ubuntu16.04-x64 app.
The rest of the system runs without problems, except the signature verification.
My validation always returns false.
Here is my service for validating the signature and body:
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace MySkill.Main.Services
{
public class CertificationValidationService : ICertificationValidationService
{
private const string Algorithm = "SHA1withRSA";
public async Task<bool> IsValidSiganture(Stream body, Stream certData, string signature)
{
var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(new StreamReader(certData));
var cert = (Org.BouncyCastle.X509.X509Certificate)pemReader.ReadObject();
using (var sr = new StreamReader(body))
{
var content = await sr.ReadToEndAsync();
var result = CheckRequestSignature(Encoding.UTF8.GetBytes(content), signature, cert);
return result;
}
}
private static bool CheckRequestSignature(byte[] bodyData, string signature, Org.BouncyCastle.X509.X509Certificate cert)
{
byte[] sig = Convert.FromBase64String(signature);
var pubKey = (Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)cert.GetPublicKey();
var signer = Org.BouncyCastle.Security.SignerUtilities.GetSigner(Algorithm);
signer.Init(false, pubKey);
signer.BlockUpdate(bodyData, 0, bodyData.Length);
return signer.VerifySignature(sig);
}
}
}
Does anybody have a tip, what i'm doing wrong or has used a different framework or apis?
From the code you shared it looks correct. Without seeing the rest of the code I am not sure if the Signature you got is correct. You can take a look at our C# code that passed certification at https://github.com/sophtron/Alexa-Skill-Sophtron and compare against your own to see if there are any differences.
I tried RSACryptoServiceProvider from .net library for signature validation and it had never worked. So I had to switch to BouncyCastle.1.8.1 too and it worked for me.
Related
There are examples on the web on how to use bouncy castle library in Java to encrypt with RSA/ECB/OAEPWithSHA256AndMGF1Padding (Example is shown at breaking down RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING). However the bouncy castle library in C# seem to have deviated from Java library in that it is more explicit (hence requires more steps) and I am not able to figure out how to make it work for the above algorithm.
Would appreciate if some body can put a code sample together to encrypt a sample text using RSA/ECB/OAEPWithSHA256AndMGF1Padding.
Unfortunately, even the Java construct is ambiguous as it's open to different and incompatible interpretations, as is shown here. The Java Bouncycastle provider will do one thing with "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" and the Oracle provider will do a different thing.
You can and should specify exactly which behavior you want in both the Java and C# code.
C#:
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
namespace ScratchPad
{
class MainClass
{
public static void OaepEncryptExample()
{
var plain = Encoding.UTF8.GetBytes("The sun also rises.");
// Read in public key from file
var pemReader = new PemReader(File.OpenText(#"/Users/horton/tmp/key-examples/myserver_pub.pem"));
var rsaPub = (Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)pemReader.ReadObject();
// create encrypter
var encrypter = new OaepEncoding(new RsaEngine(), new Sha256Digest(), new Sha256Digest(), null);
encrypter.Init(true, rsaPub);
var cipher = encrypter.ProcessBlock(plain, 0, plain.Length);
Console.WriteLine(Convert.ToBase64String(cipher));
}
}
}
Java:
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.io.FileReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class OaepExample {
public static void oeapDecrypt() throws Exception {
final PEMParser pemParser = new PEMParser(new FileReader("/Users/horton/tmp/key-examples/myserver.p8"));
final PrivateKeyInfo privKey = (PrivateKeyInfo) pemParser.readObject();
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey rsaPriv = (RSAPrivateKey) kf.generatePrivate(new PKCS8EncodedKeySpec(privKey.getEncoded()));
String cipher64 = "k8AYnTV6RgzQXmD7qn8QwucDXGjbYct+qMVvDmMELTnUcCOeTp82oJ0BryZyEEGXVSZ2BFg95e72Jt9ZAKWNcot2rZ0+POcda8pzY/MfdwIpnSJKITovk8xHL3B/jZDJyQrLMmNPjVV/uBFY2vgKhhLhJzzAJATcGpNdw+gF+XI=";
Cipher decrypter = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
OAEPParameterSpec parameterSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT);
decrypter.init(Cipher.DECRYPT_MODE, rsaPriv, parameterSpec);
final byte[] plain = decrypter.doFinal(Base64.getDecoder().decode(cipher64));
System.out.println(new String(plain, StandardCharsets.UTF_8));
}
}
I am writing a Azure Function for PDF conversion with dependencies on DataLogics PDF conversion and a Nuget package (mlkpwgen) for password generation.
Functions are
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;
using System;
using MlkPwgen;
using Datalogics.PDFL;
using System.Diagnostics;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("Function1")]
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
PDFConversion();
string requestBody = new StreamReader(req.Body).ReadToEnd();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
public static string PDFConversion()
{
using (Library lib = new Library())
{
String sInput = #"C:\Users\Kunal\Downloads\Indian Management.pdf";
String sOutput = #"C:\Users\Kunal\Downloads\WatermarkedOutput.pdf";
Document doc = new Document(sInput);
string ownerPassword = PasswordGenerator.Generate(length: 32);
string userPassword = PasswordGenerator.Generate(length: 32);
doc.Secure(PermissionFlags.Print | PermissionFlags.HighPrint, ownerPassword, userPassword);
WatermarkParams watermarkParams = new WatermarkParams();
watermarkParams.Rotation = 45.3f;
watermarkParams.Opacity = 0.15f;
watermarkParams.TargetRange.PageSpec = PageSpec.AllPages;
WatermarkTextParams watermarkTextParams = new WatermarkTextParams();
Color color = new Color(0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f);
watermarkTextParams.Color = color;
watermarkTextParams.Text = "Centre Code - Unit - 0101";
Font f = new Font("Arial", FontCreateFlags.Embedded | FontCreateFlags.Subset);
watermarkTextParams.Font = f;
watermarkTextParams.FontSize = 80f;
watermarkTextParams.TextAlign = HorizontalAlignment.Center;
doc.Watermark(watermarkTextParams, watermarkParams);
doc.EmbedFonts();
doc.Save(SaveFlags.Full | SaveFlags.Linearized, sOutput);
Process.Start(#"C:\Users\Kunal\Downloads\WatermarkedOutput.pdf");
return sInput;
}
}
}
}
I am getting the following Exception
"System.Private.CoreLib: Exception while executing function:
Function1. Datalogics.PDFL: The type initializer for
'Datalogics.PDFL.PDFLPINVOKE' threw an exception. Datalogics.PDFL: The
type initializer for 'SWIGExceptionHelper' threw an exception.
Datalogics.PDFL: Unable to load DLL 'DL150PDFLPINVOKE': The specified
module could not be found. (Exception from HRESULT: 0x8007007E)."
The same code works fine as a Console application. What am I missing here?
If fixing the hard-coded file names still doesn't help, the error sounds like a permission exception.
Azure Functions run on App Service, which has a sandbox for all the code, where some calls are not allowed. E.g. GDI32 which is used extensively by PDF generation libraries.
Read more in Azure Web App sandbox.
Thanks for reading through the question and trying to answer.
I found that even after adding reference to the Datalogics.PDFL.dll, the code was failing.
So i copied all the other dll's into the bin\debug folder and now the code works fine
DL150ACE.dll
DL150AdobeXMP.dll
DL150AGM.dll
DL150ARE.dll
DL150AXE8SharedExpat.dll
DL150BIB.dll
DL150BIBUtils.dll
DL150CoolType.dll
DL150JP2KLib.dll
DL150PDFL.dll
DL150PDFLPINVOKE.dll
DL150pdfport.dll
DL150pdfsettings.dll
DotNETViewerComponent.dll
Per this MS Forums post:
Azure Functions does not provide support for loading native binaries in its current release. Even if we were able to install this package, you may still encounter errors when those native dlls are loaded during runtime.
So this is expected behavior when trying to call native binaries. Please contact our Support department if you have any more questions about getting started using the PDF Library.
So i am fiddling with this website's IPN function to see if i wan't to incorporate it to some dumb project my friends and i are working on. To be honest i don't know much C# in depth, i'm fairly new to the language (a few months of coding practice).
This is the PHP sample they give out on how to use it:
https://github.com/Rocketr/rocketrnet-ipn-php/blob/master/example_rocketr_ipn.php
I am trying to make a receiver like that in MVC 5. I have the Model setup with a function when the IPN hits the server page to process the request but it seems to just fail out everytime and not write any raw data i am trying to capture to the logs.
// GET: Purchases/Incoming
public void Incoming()
{
var ipnDebugLog = HttpRuntime.AppDomainAppPath + "/Logs/IPN/debug.txt";
var testIPNKey = "the hash here";
byte[] ipnToByes = Encoding.ASCII.GetBytes(testIPNKey); // IPN string to byteto hash with
var recvdIPN = Request["HTTP_IPN_HASH"];
HMACSHA256 testHash = new HMACSHA256(ipnToByes); // Setting testHash to IPN secret string
string ipnHeader = Request["IPN_HASH"];
using (StreamWriter sw = new StreamWriter(ipnDebugLog))
{
sw.WriteLine(ipnHeader);
foreach (var reqHead in ipnHeader)
{
sw.WriteLine(reqHead.ToString());
sw.WriteLine("JSON String: " + Request["HTTP_IPN_SECRET"]);
sw.WriteLine(recvdIPN);
sw.WriteLine("From: " + GetIPAddress());
}
}
}
So this is just me trying to get the data being sent from Rocketr. On the site it states:
To verify the integrity of the payload, we will send an HMAC signature
over a HTTP header called “IPN_HASH”. The HMAC signature will be a
signed json encoded POST object with your IPN secret. You can see how
to verify the signature in the example_rocketr_ipn.php file in this
repository.
Am i just to dumb and new to understand C# to function like this? I feel like i'm on the right track to reading the raw data but i'm probly wrong?
So to sum up the question
Am i doing the incorrect way to read a raw custom HTTP header called IPN_HASH? Going off of the PHP example they used isset to read a server variable header labled HTTP_IPN_HASH right?
So i have to convert this $hmac = hash_hmac("sha512", json_encode($_POST), trim($IPN_SECRET));
Try this (make adjustments as needed/necessary):
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
namespace Foo
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void PhpHashTest()
{
string IPN_SECRET = "I-am-the-secret";
//Mocking some HTTP POSTed data
var someFormUrlEncodedData = "order_id=1234&product_title=Hello%20World&product_id=Sku123";
//Mocking json_encode($_POST)
var data = HttpUtility.ParseQueryString(someFormUrlEncodedData);
var dictionary = data.AllKeys.ToDictionary(key => key, key => data[key]);
//{"order_id":"1234","product_title":"Hello World","product_id":"Sku123"}
var json = JsonConvert.SerializeObject(dictionary);
byte[] bytes;
using (var hmac512 = new HMACSHA512(Encoding.ASCII.GetBytes(IPN_SECRET)))
{
bytes = hmac512.ComputeHash(Encoding.ASCII.GetBytes(json));
}
//will contain lower-case hex like Php hash_hmac
var hash = new StringBuilder();
Array.ForEach(bytes, b => hash.Append(b.ToString("x2")));
//Assert that Php output exactly matches implementation
Assert.IsTrue(string.Equals("340c0049bde54aa0d34ea180f8e015c96edfc4d4a6cbd7f139d80df9669237c3d564f10366f3549a61871779c2a20d2512c364ee56af18a25f70b89bd8b07421", hash.ToString(), StringComparison.Ordinal));
Console.WriteLine(hash);
}
}
}
Not a PHP dev - this is my "Php version":
<?php
$IPN_SECRET = 'I-am-the-secret';
# 'order_id=1234&product_title=Hello%20World&product_id=Sku123';
$json = array('order_id' => '1234', 'product_title' => 'Hello World', 'product_id' =>'Sku123');
echo json_encode($json);
echo "<br />";
$hmac = hash_hmac("sha512", json_encode($json), trim($IPN_SECRET));
echo $hmac;
?>
Hth....
I have to dare with a web application developed in .NET 1.1 Framework, with no possibilities to upgrade to major versions.
Having said that, I need to encrypt a text using HMAC SHA256.
I see that System.Security.Cryptography namespace in .NET 1.1 provides me a way to has a message in SHA256. But I need to use HMAC (Hash-based Message Authentication Code) with SHA256, so I send not only the text to encrypt, but also a key.
I see that .NET Framework 2.0 and later has an specific class HMACSHA256 to manage this. But haven't found an implementation for .NET 1.1.
¿Any help?
Thanks in advance
You can add to your project the file from Microsoft:
namespace System.Security.Cryptography {
[System.Runtime.InteropServices.ComVisible(true)]
public class HMACSHA256 : HMAC {
//
// public constructors
//
public HMACSHA256 () : this (Utils.GenerateRandom(64)) {}
public HMACSHA256 (byte[] key) {
m_hashName = "SHA256";
#if FEATURE_CRYPTO
m_hash1 = GetHashAlgorithmWithFipsFallback(() => new SHA256Managed(), () => HashAlgorithm.Create("System.Security.Cryptography.SHA256CryptoServiceProvider"));
m_hash2 = GetHashAlgorithmWithFipsFallback(() => new SHA256Managed(), () => HashAlgorithm.Create("System.Security.Cryptography.SHA256CryptoServiceProvider"));
#else
m_hash1 = new SHA256Managed();
m_hash2 = new SHA256Managed();
#endif // FEATURE_CRYPTO
HashSizeValue = 256;
base.InitializeKey(key);
}
}
}
It seems that all calls are based on class that exists in .Net 1.1
We're trying to generate an X509 certificate (including the private key) programmatically using C# and the BouncyCastle library. We've tried using some of the code from this sample by Felix Kollmann but the private key part of the certificate returns null. Code and unit test are as below:
using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace MyApp
{
public class CertificateGenerator
{
/// <summary>
///
/// </summary>
/// <remarks>Based on <see cref="http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx"/></remarks>
/// <param name="subjectName"></param>
/// <returns></returns>
public static byte[] GenerateCertificate(string subjectName)
{
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
var kp = kpgen.GenerateKeyPair();
var gen = new X509V3CertificateGenerator();
var certName = new X509Name("CN=" + subjectName);
var serialNo = BigInteger.ProbablePrime(120, new Random());
gen.SetSerialNumber(serialNo);
gen.SetSubjectDN(certName);
gen.SetIssuerDN(certName);
gen.SetNotAfter(DateTime.Now.AddYears(100));
gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
gen.SetSignatureAlgorithm("MD5WithRSA");
gen.SetPublicKey(kp.Public);
gen.AddExtension(
X509Extensions.AuthorityKeyIdentifier.Id,
false,
new AuthorityKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public),
new GeneralNames(new GeneralName(certName)),
serialNo));
gen.AddExtension(
X509Extensions.ExtendedKeyUsage.Id,
false,
new ExtendedKeyUsage(new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") }));
var newCert = gen.Generate(kp.Private);
return DotNetUtilities.ToX509Certificate(newCert).Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "password");
}
}
}
Unit test:
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyApp
{
[TestClass]
public class CertificateGeneratorTests
{
[TestMethod]
public void GenerateCertificate_Test_ValidCertificate()
{
// Arrange
string subjectName = "test";
// Act
byte[] actual = CertificateGenerator.GenerateCertificate(subjectName);
// Assert
var cert = new X509Certificate2(actual, "password");
Assert.AreEqual("CN=" + subjectName, cert.Subject);
Assert.IsInstanceOfType(cert.PrivateKey, typeof(RSACryptoServiceProvider));
}
}
}
Just to clarify, an X.509 certificate does not contain the private key. The word certificate is sometimes misused to represent the combination of the certificate and the private key, but they are two distinct entities. The whole point of using certificates is to send them more or less openly, without sending the private key, which must be kept secret. An X509Certificate2 object may have a private key associated with it (via its PrivateKey property), but that's only a convenience as part of the design of this class.
In your first BouncyCastle code example, newCert is really just the certificate and DotNetUtilities.ToX509Certificate(newCert) is built from the certificate only.
Considering that the PKCS#12 format requires the presence of a private key, I'm quite surprised that the following part even works (considering you're calling it on a certificate which can't possibly know the private key):
.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12,
"password");
(gen.Generate(kp.Private) signs the certificate using the private key, but doesn't put the private key in the certificate, which wouldn't make sense.)
If you want your method to return both the certificate and the private key you could either:
Return an X509Certificate2 object in which you've initialized the PrivateKey property
Build a PKCS#12 store and returns its byte[] content (as if it was a file). Step 3 in the link you've sent (mirror) explains how to build a PKCS#12 store.
Returning the byte[] (DER) structure for the X.509 certificate itself will not contain the private key.
If your main concern (according to your test case) is to check that the certificate was built from an RSA key-pair, you can check the type of its public key instead.
I realise this is an old post but I found these excellent articles which go through the process:
Using Bouncy Castle from .NET