Using this command:
openssl smime -encrypt -aes-256-cbc -in input.txt -out output.txt -outform DER yourSslCertificate.pem
I can decrypt (using my private key) an encrypted file (encrypted with my public key). Is there a direct translation to this not using OpenSSL but only .Net 4.5 Framework (or lower) classes. I can use Bouncy Castle if it helps.
The private key was created using openssl:
openssl req -x509 -nodes -days 100000 -newkey rsa:8912 -keyout private_key.pem -out certificate.pem
The EnvelopedCms class is the built in version, especially if you want DER output.
using System.Security.Cryptography.Pkcs;
...
ContentInfo content = new ContentInfo(File.ReadAllBytes("input.txt"));
EnvelopedCms cms = new EnvelopedCms(content);
// Change the encryption algorithm to AES-256. Not needed when decrypting.
cms.ContentEncryptionAlgorithm.Oid = new Oid("2.16.840.1.101.3.4.1.42", null);
using (X509Certificate2 cert = new X509Certificate2("yourSslCertificate.pem"))
{
cms.Encrypt(new CmsRecipient(SubjectIdentifierType.SubjectKeyIdentifier, cert));
}
File.WriteAllBytes("output.txt", cms.Encode());
This method did the trick.
openssl smime -encrypt -aes-256-cbc -in input.txt -out output.txt -outform DER yourSslCertificate.pem
public static void DecryptFileWithX509PrivateCertificate(
string inputFilePath, --path to input.txt
string outputFilePath, --path to output.txt
string privateKeyFilePath --path to yourSslCertificate.pem
)
{
byte[] encryptedData = File.ReadAllBytes(inputFilePath);
CmsEnvelopedDataParser parser = new CmsEnvelopedDataParser(encryptedData);
RecipientInformationStore recipients = parser.GetRecipientInfos();
PemReader pemReader = new PemReader(File.OpenText(privateKeyFilePath));
RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
foreach (RecipientInformation recipient in recipients.GetRecipients())
{
byte[] decryptedData = recipient.GetContent(privateKey);
File.WriteAllBytes(outputFilePath, decryptedData);
break;
}
}
Related
To generate a private and public key with OpenSSL I have run
openssl ecparam -genkey -name secp256k1 -out private-key.pem
openssl ec -in private-key.pem -out public-key.pem -pubout
I upload the public key to the remote server. Then import the private-key.pem to a .Net Framework (it has to be .Net Framework) C# service to use it to sign an API payload:
public string LoadFromCng(byte[] request, string privateKeyFile)
{
CngKey cng = CngKey.Open(privateKeyFile);
// Sign the request body with the private key.
ECDsaCng dsa = new ECDsaCng(cng);
byte[] signedRequest = dsa.SignData(request, HashAlgorithmName.SHA256);
return Convert.ToBase64String(signedRequest);
}
With privateKeyFile = private-key.pem the above code gives Keyset does not exist
If I use mkcert and run mkcert -ecdsa -pkcs12 private-key.pem it generates a PKCS#12 key called private-key.pem.p12 and then:
public string LoadFromX509(byte[] request, string privateKeyFile)
{
var cert = new X509Certificate2(privateKeyFile, "changeit");
var key = cert.GetECDsaPrivateKey();
byte[] signedRequest = key.SignData(request, HashAlgorithmName.SHA256);
return Convert.ToBase64String(signedRequest);
}
With privateKeyFile = private-key.pem.p12 the above code appears to sign the request, but the API response is The remote server returned an error: (400) Bad Request which means the API provider can't decode the payload from the public key.
I get the same 400 error when going through the cheat sheet here and creating an X509 pfx certificate.
openssl req -new -x509 -key private-key.pem -out cert.pem -days 360
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx
The method above appears to sign the payload but the provider responds with a 400.
The suggestions here and here and here and here and others have not worked.
I can't use Net Core or NET 5 so this doesn't work either. The ImportPkcs8PrivateKey method is not available in Net Framework.
If I try and use Bouncy Castle per here I get Unable to cast object of type 'Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters' to type 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters'
The first OpenSSL statement creates an EC parameter file private-key.pem, which contains, among others, a PEM encoded private EC key in SEC1 format.
With .NET Framework the easiest way is to use BouncyCastle for key import. BouncyCastle also supports the ASN.1/DER format and the IEEE P1363 (r|s) format for EC signatures.
The following code imports a PEM encoded private EC key in SEC1 format and signs a message.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
...
string pkcs8 = #"-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIMmN/YG6BtIqm9eAcYtKdcbJosNKbB76vGMTSltPLNiioAcGBSuBBAAK
oUQDQgAESGnGxuBPdrwv1TkJEorsaNk74+ZFh2jzww2SpLTqQqkvOf5IP6fuODS/
hzztSBpsBpX9LUZh8TYHX0HRMagkaA==
-----END EC PRIVATE KEY-----";
TextReader privateKeyTextReader = new StringReader(pkcs8);
AsymmetricCipherKeyPair privateKeyParams = (AsymmetricCipherKeyPair)new PemReader(privateKeyTextReader).ReadObject();
byte[] message = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA"); // ASN.1/DER format
//ISigner signer = SignerUtilities.GetSigner("SHA-256withPLAIN-ECDSA"); // r|s format
signer.Init(true, privateKeyParams.Private);
signer.BlockUpdate(message, 0, message.Length);
byte[] signature = signer.GenerateSignature();
Console.WriteLine(Hex.ToHexString(signature)); // 3046022100967c2e890d933c75468dceaf61152add41f143568dcb967583e1b307a0b495e30221008f6837c0d9cc01fc7bfe54ed5b6267fd3e64c2d3ff771e72762f24c20071c454
I'm trying to generate RSA X509 public/private key to dynamically,
below is how I do it with openssh command line:
openssl genrsa -out privatekey.pem 1024
openssl req -new -x509 -key privatekey.pem -out publickey.cer -days 1825
openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer
Also how do I add passphrase to encrypt the private key?
and I only got here by far
//Generate a public/private key pair.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//Save the public key information to an RSAParameters structure.
RSAParameters rsaKeyInfo = rsa.ExportParameters(true);
I have used this class however it does not result into a valid SSL, when I submit it to developer portal it did not get accepted as valid public key: developer.xero.com/myapps
Regards
Note that I have replaced the RSACryptoServiceProvider class with the recommended RSA base class which is cross-platform and also the better RSA implementation.
This SO question put me in the right direction.
using (var rsa = RSA.Create(1024))
{
var distinguishedName = new X500DistinguishedName($"CN=SelfSignedCertificate");
var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);
var certificate = request.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1825));
// Create PFX (PKCS #12) with private key
File.WriteAllBytes("privatekey.pfx", certificate.Export(X509ContentType.Pfx, "RGliXtaLkENste"));
// Create Base 64 encoded CER (public key only)
File.WriteAllText("publickey.cer",
"-----BEGIN CERTIFICATE-----\r\n"
+ Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
+ "\r\n-----END CERTIFICATE-----");
}
I have tested the resulting .cer file on xero so it should work
I have two applications. One that signs a file and the other that verifies.
The signing application does the following:
X509Certificate2 certificate = new X509Certificate2(PROJECT_DIR_PATH + "cert.pfx", "password");
using (RSA rsa = certificate.GetRSAPrivateKey())
{
signature = rsa.SignData(exeContent, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
cert.pfx is a self-signed certificate, generated with Openssl.
The verifying application:
X509Certificate2 certificate = new X509Certificate2(PROJECT_DIR_PATH + "cert.pfx", "password");
using (RSA rsa = certificate.GetRSAPublicKey())
{
return rsa.VerifyData(exeContentWithoutSignature, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
As I understand the .pfx file contains public and private key information, thus I should not make it available to anyone. As I know, only the public key is needed for the verification step.
How can I use rsa.VerifyData or other functions to verify the signature without exposing my pfx file?
I already had .pfx file that can be generated this way:
openssl req -x509 -days 365 -newkey rsa:2048 -keyout test-key.pem -out test-cert.pem
openssl pkcs12 -export -in test-cert.pem -inkey test-key.pem -out test-cert.pfx
In order to extract a certificate containing only public key following command can be used:
openssl pkcs12 -in test-cert.pfx -clcerts -nokeys -out cert.pem
-clcerts - Only output client certificates.
-nokeys - Don't output private keys.
Cert.pem can be used to create an instance of X509Certificate2:
X509Certificate2 certificate = new X509Certificate2(PROJECT_DIR_PATH + "test-cert.pem");
using (RSA rsa = certificate.GetRSAPublicKey())
{
return rsa.VerifyData(exeContentWithoutSignature, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
I am trying to use a self-signed certificate to configure IdentityServer3,
I used openssl to create a x509 certificate as follows:
openssl req -x509 -sha256 -days 365 -key key.pem -in csr.csr -out certificate.pem
then merged the key and cert using :
pkcs12 -export -in my-cert.pem inkey my-key.pem -out xyz-cert.pfx
i then converted the content of xyz-cert.pfx to Base64String which is stored in a key in web.config, then tried to use the certificate to instantiate X509Certificate2 as follows:
var certificate = Convert.FromBase64String(ConfigurationManager.AppSettings["SigningCertificate"]);
var options = new IdentityServerOptions
{
SigningCertificate = new X509Certificate2(certificate, ConfigurationManager.AppSettings["SigningCertificatePassword"]),
RequireSsl = false, // DO NOT DO THIS IN
Factory = factory
};
the following exception is then thrown:
I can't figure out where i got it wrong.
Thanks for your help
var options = new IdentityServerOptions
{
string CertText = ConfigurationManager.AppSettings["SigningCertificatePassword"];
byte[] certBytes = Convert.FromBase64String(certText);
SigningCertificate = new X509Certificate2(certificate, certBytes),
RequireSsl = false, // DO NOT DO THIS IN
Factory = factory
};
As a quick overview I am attempting to generate a ES256 algorithm -JWT token via C# using the https://github.com/dvsekhvalnov/jose-jwt library.
As the directions state:
ES256, ES384, ES256 ECDSA signatures requires CngKey (usually private)
elliptic curve key of corresponding length. Normally existing CngKey
loaded via CngKey.Open(..) method from Key Storage Provider. But if
you want to use raw key material (x,y) and d, jose-jwt provides
convenient helper EccKey.New(x,y,d).
The CngKey.Open() states it opens an existing key, but by the sounds of it I should be using the CngKey.Import() instead? When I attempt to call the CngKey.Import() it returns the following error:
The parameter is incorrect.
Basically what I am asking is what is the simplest way to convert an existing PEM file into the CngKey object which is required for the Jose.JWT.Encode() function? Any help would be highly appreciated. Thanks!
Below is my code(for security purposed that is not the real private key):
public string GenerateToken(int contactID, Database _db)
{
var contact = GetContact(contactID, _db);
var payload = new Dictionary<string, object>()
{
{"broker", 1},
{"contact_id", contact.id},
{"name", contact.fname + " " + contact.lname + ""},
{"iss", "www.somewhere.com"},
{"iat", (DateTime.Now - UnixEpoch).TotalSeconds},
{"nbf", (DateTime.Now - UnixEpoch).TotalSeconds},
{"exp", (DateTime.Now.AddDays(30) - UnixEpoch).TotalSeconds}
};
string privateKey =
"MHcCAQEffEIIIHHHHHHHHHHHHHHHffHHHHHHHHHHHHHHHHHHHHHHHoGgCCqGSM49" +
"AwEHhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhI+pRkAxAb13" +
"77vz2Yjjjjjjjjjjjjjjjjjjjjw==";
byte[] b = Convert.FromBase64String(privateKey);
CngKey cng = CngKey.Import(b, CngKeyBlobFormat.EccPrivateBlob);
string token = Jose.JWT.Encode(payload, cng, JwsAlgorithm.ES256);
return token;
}
I had the same problem with jose-jwt and got it working using my own implementation of GetECDsaPrivateKey(). Note that your project should target .NET 4.6.1. Please follow the steps below:
1.Generate a p12 X509Certificate2 using openssl
> openssl ecparam -name prime256v1 -genkey > private-key.pem
> openssl ec -in private-key.pem -pubout -out public-key.pem
> openssl req -new -key private-key.pem -x509 -nodes -days 365 -out public.cer
> winpty openssl pkcs12 -export -in public.cer -inkey private-key.pem -out publiccert.p12
2.Generate a JWT by reading private key from above generated certificate:
var claims = new Dictionary<string, object>()
{
{ "sub", "mr.x#contoso.com" },
{ "exp", 1300819380 }
};
var certificate = new X509Certificate2("publiccert.p12", "passcode");
string token = SignJWTWithCert(certificate, claims);
private static string SignJWTWithCert(X509Certificate2 cert, object claims)
{
var header = new { alg = "ES256", typ = "JWT" };
byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
byte[] claimsBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(claims, Formatting.None));
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
{
if (ecdsa == null)
throw new ArgumentException("Cert must have an ECDSA private key", nameof(cert));
var payload = Base64UrlEncode(headerBytes) + "." + Base64UrlEncode(claimsBytes);
var signature = ecdsa.SignData(Encoding.UTF8.GetBytes(payload), HashAlgorithmName.SHA256);
return payload + "." + Base64UrlEncode(signature);
}
}