I have an Apache (xampp/wamp) server that provides a SSL connection on port 443.
It uses two certificate files: server.cert and server.key when the latter conains the private key.
I have another server configured to listen to requests on port 843 (flash policy stuff) and response to a certain request with some text reply written in C# which runs separately.
In order to achieve SSL connectivity, i use a flex object called SecureSocket which allowes that, however, it uses the original servers certificate in order to encrypt the request.
My goal is to teach my 843 C# server to decrypt the sent data and encrypt the reply and for this i'm using the X509Certificate object in C#.
However, since the pub and priv keys are on different files, i'm getting FALSE on the following:
string text = System.IO.File.ReadAllText(#"C:\xampp\apache\conf\ssl.crt\server.crt");
UTF8Encoding encoding = new System.Text.UTF8Encoding();
byte[] byteCert = encoding.GetBytes(text);
X509Certificate2 uberCert = new X509Certificate2();
uberCert.Import(byteCert);
Console.WriteLine("Has privateKey:" + uberCert.HasPrivateKey.ToString());
Console.WriteLine("PrivateKey: \n" + uberCert.PrivateKey);
Obviously, the False on uberCert.HasPrivateKey comes from the fact that the private key is on a different file, so my questions are:
1.How can i read the private key using the X509CErtificate2 object?
2.How can i use the public key in order to decrypt the received message and how to re-encrypt it with the private key (in order to send the encrypted response back) ?
Thanks in advance,
Mike.
I've created a small helper NuGet package (based on opensslkey) to create a X509 certificate based on public key and private (rsa) key.
// Generate with: openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt
string certificateText = File.ReadAllText("certificate_pub.crt");
string privateKeyText = File.ReadAllText("private.key");
ICertificateProvider provider = new CertificateFromFileProvider(certificateText, privateKeyText);
X509Certificate2 certificate = provider.Certificate;
// Example: use the PrivateKey from the certificate above for signing a JWT token using Jose.Jwt:
string token = Jose.JWT.Encode(payload, certificate.PrivateKey, JwsAlgorithm.RS256);
See NuGet and Github-project for functionality and code-examples.
The private key is likely PEM encoded PKCS#8 structure.
The Mono project provides code to read this format (among other) in the Mono.Security.dll assembly. This assembly is purely managed and will work on Windows, Linux or OSX.
You can't in straight .NET.
You can either use BouncyCastle (see this answer: How to read a PEM RSA private key from .NET) or use a PKCS12 container instead for the username + password, which you can create using OpenSSL's tools from the PEM files.
Related
For the project I am working on I have to convert the following legacy code to use a SHA-384 ECDSA certificate, .NET Framework 4.8 as target.
Is it possible natively in C# or do I need some help like BouncyCastle lib and how?
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
[...]
// Certificate is in PKCS#12 format and contains a private key, certificate is password protected with the session id
var certificateBytes = Convert.FromBase64String(base64Certificate.ToString());
var certificate = new X509Certificate2();
certificate.Import(certificateBytes, sessionId, X509KeyStorageFlags.DefaultKeySet);
var result = (HttpWebRequest)WebRequest.Create( url );
result.Headers.Add( "x-jwt-authorization", $"Bearer {JSONWebToken}" );
result.ClientCertificates.Add( Certificate );
var response = (HttpWebResponse)request.GetResponse();
I tried to find a solution/explanation online but didn't work (keep getting a "Not supported" for the PrivateKey property when trying to import the certificate, and a 403 Forbidden as server response).
Also if I want to keep backward compatibility, how do I distinguish if the Base64 string contains a X509 or a SHA one to support both?
I have 2 approaches to do the same thing, but Azure has deprecated the one that works, and the other method doesn't work.
The approach that works, but is deprecated:
I store my PFX in Azure Key Vault Secrets. (when I create the secret I see a warning stating that this feature is deprecated)
and use the following code to retrieve it to create my certificate:
SecretBundle secret = await keyVaultClient.GetSecretAsync(keyVaultUrl, "MyCert-Secret");
X509Certificate2Collection exportedCertCollection = new X509Certificate2Collection();
exportedCertCollection.Import(Convert.FromBase64String(secret.Value));
X509Certificate2 certFromSecret = exportedCertCollection.Cast<X509Certificate2>().Single(s => s.HasPrivateKey);
credits to this answer
I'm able to use this certificate to host and access my application successfully.
The approach that doesn't work, but I should be using:
I store my certificate in the Azure Key vault Certificates
and use the following code to retrieve it and create the X509Certificate2:
CertificateBundle certificateBundle = await keyVaultClient.GetCertificateAsync(keyVaultUrl, "MyCert-Certificate");
X509Certificate2 certFromCertificate = new X509Certificate2(certificateBundle.Cer);
The problem with this approach is that the certificate does not contain the private key. i.e. certFromCertificate.HasPrivateKey is false.
My Questions
Why does certFromSecret have the PrivateKey, while certFromCertificate doesn't?
How can I retrieve a certificate from the key vault, where I can create a X509Certificate2 object to host my application in Kestrel with UseHttps.
The 2nd part of #Adrian's answer explains the concepts around the Azure KV Certificates very well, and I have changed my code as below to get the full certificate including the private keys:
SecretBundle secret = await kv.GetSecretAsync(keyVaultUrl, certName);
X509Certificate2 certificate =
new X509Certificate2(Convert.FromBase64String(secret.Value));
The trick was to use GetSecretAsync instead of GetCertificateAsync. Please refer to Adrian's SO answer to see why the secret had to be used to get the full certificate with Private keys.
Note that you should use "Certificate identifier" property (url with "/secrets/") from Azure certificate's property page.
The latest version of the SDK (Azure.Security.KeyVault.Certificates 4.2.0) now has the DownloadCertificateAsync method, which obtains the full cert (i.e. private key too) in a straightforward way.
The documentation states:
Because Cer contains only the public key, this method attempts to
download the managed secret that contains the full certificate.
X509Certificate2 cert = await certificateClient.DownloadCertificateAsync(certName);
I am trying to create a self signed RSA-2048-SHA-256 certificate PFX file, in order to use it for data signing in my WCF requests.
I used some openSSL examples in order to create a certificate PFX file, but even though I set the SHA algorithm to 256, when I load it in my .net app, I see that this certificate's private key, has these settings:
KeyExchangeAlgorithm = RSA-PKCS1-KeyEx
SignatureAlgorithm = http://www.w3.org/2000/09/xmldsig#rsa-sha1
and when I use the code below in order to consume this certificate, I am getting "Invalid algorithm specified exception", but if I change the SHA256CryptoServiceProvider to SHA1CryptoServiceProvider everything works fine.
string msg = "This is my test message";
X509Certificate2 privateCert = new X509Certificate2("C:\\TEMP\\private.pfx", "12345");
byte[] signature = (privateCert.PrivateKey as RSACryptoServiceProvider).SignData(new UTF8Encoding().GetBytes(msg), new SHA256CryptoServiceProvider());
I can only assume that my certificate file was not created with SHA256, but instead with some kind of default SHA1 algorithm.
Those are the steps I used in order to create my certificate:
openssl req -x509 -days 365 -newkey rsa:2048 -sha256 -keyout key.pem -out cert.pem
openssl pkcs12 -export -in cert.pem -inkey key.pem -out private.pfx
What am I doing wrong?
What am I doing wrong?
Believing that those two properties have meaning :).
The two values you're seeing are hard-coded into RSACryptoServiceProvider. Other RSA types (such as RSACng) have different, less confusing, hard-coded values.
The problem is that a key doesn't have either of those attributes. A key can be used for multiple purposes (though NIST recommends against it). A TLS session (or EnvelopedCMS document, etc) can have a key exchange algorithm. A certificate, SignedCMS document, or other such material can have a signature (and thus a signature algorithm).
To know that your certificate was signed with RSA-PKCS1-SHA256 you need to look at X509Certificate2.SignatureAlgorithm.
switch (cert.SignatureAlgorithm.Value)
{
case "1.2.840.113549.1.1.4":
return "RSA-PKCS1-MD5";
case "1.2.840.113549.1.1.5";
return "RSA-PKCS1-SHA1";
case "1.2.840.113549.1.1.11";
return "RSA-PKCS1-SHA2-256"; // Winner
case "1.2.840.113549.1.1.12":
return "RSA-PKCS1-SHA2-384";
case "1.2.840.113549.1.1.13":
return "RSA-PKCS1-SHA2-512";
default:
throw new SomethingFromTheFutureOrMaybeNotRSAException();
}
I am trying to figure out a way of authentication between two distributed services.
I don't want to have a shared secret distributed on every service host, because it would mean that once one host has been compromised, all hosts are compromised.
So my scenario is:
Host A knows the public key of Host B
Host A encodes and encryptes the jwt using Host B´s public key
Host B receives and decrypts the jwt using its private key, that it only knows itself.
The jose-jwt package:
https://github.com/dvsekhvalnov/jose-jwt
seems like a good option to me. Beside the signing of the jwt, it also supports encryption using private/public keys.
On the page there are the following examples for encoding and decoding a jwt:
Encode:
var publicKey=new X509Certificate2("my-key.p12", "password").PublicKey.Key as RSACryptoServiceProvider;
string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);
Decode:
var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;
string json = Jose.JWT.Decode(token,privateKey);
Now, here is what i don´t understand:
How can I create a .p12 certificate file that only contains the public key information (for the host/service A that encodes the jwt) ?
.. and how can I create a .p12 certificate file that contains both, the public and the private key information (for the host/service B that decodes the jwt) ?
From all the research that I have done, i get the impression that you can either only make a .p12 file that contains both, or one that contains only the public key. But it seems there is no way to create two .p12 files, one with both information and one with only the public key. What am I missing?
Thanks for your answers.
Normally a PKCS12/PFX is not used for public-only, but you can do it if you like.
Assuming that cert.HasPrivateKey is true: cert.Export(X509ContentType.Pkcs12, somePassword) will produce a byte[] that you can write to "publicAndPrivate.p12" (or whatever).
Normally for a public-only certificate you'll write it down just as the X.509 data, either DER-binary or PEM-DER encoded. .NET doesn't make PEM-DER easy, so we'll stick with DER-binary. You can get that data by either cert.RawData, or cert.Export(X509ContentType.Cert) (both will produce identical results, since this export form has no random data in it). (publicOnly.cer)
If you really want a PKCS12 blob which has just the public certificate:
using (X509Certificate2 publicOnly = new X509Certificate2(publicPrivate.RawData))
{
return publicOnly.Export(X509ContentType.Pkcs12, somePassword);
}
The resulting byte[] could then be publicOnly.p12.
I was recently assigned to task to encrypt some data and send it to our server. I found some valuable resources on the internet but they all require a public key, private key and secret password for the encryption. Please is there any simple way I can encrypt with just pgp key, since that's what I was given?
I assume that you have been given a public OpenPGP key. This one is enough to do encryption of data, which is intended to be decrypted by the person who gave you his public key.
In .NET you can use BouncyCastle or OpenPGPBlackbox package of our SecureBlackbox product. SecureBlackbox comes with extensive samples and support is offered as well (unlike alternatives).
Public PGP keys can encrypt data and verify signatures. Private PGP keys can decrypt data and sign data. If you have someone's public key, just use it, it won't prompt you for a password.
PGP simply works with pairs of private and public keys. The secret password is optional as far as i know.
The standard PGP encryption process in any language works as follows:
Step 1: Generate your private / public key pair
First generate your OpenPGP key pair e.g. with gnupg. The pair consists of a public key, which is used by the sender to encrypt the data and the private key, which is used by the recipient to decrypt the data.
Step 2: Share your public key and collect public keys of other parties
Each side will need to have the public keys of all the other parties. To do this step, you can give your public key using an usb stick or you upload it to a public key server.
Step 3: Encrypt and send your data
You write your data and encrypt it for the recipients.
You might also sign the data, which guarantees that the recipient can verify that the data has been created by you. After the encryption you send the data to the recipients.
Step 4: Authentication of data
You don't have to do this step but another benefit of asymmetric encryption such as PGP is that it allows for authentication. After you have exchanged public keys with your partners, the private keys can be used to digitally sign the encrypted content, allowing the decrypting site to verify the authenticity of the sender.
After data encryption is completed with the private key, you encrypt the session key with the public key that's known to the recipient (and maybe other parties as well). After that you can optionally create a hash of the encrypted data and sign this hash with your private key, this is called a signature.
Save the data in, for example, OpenPGP format.
Step 5: Decrypt data and verify signature
If you receive a data you decrypt it and if the data is signed, you verify the signature to be sure the data is sent by the sender to whom you have the public key.
Recently, I'm doing the PGP Encryption and sending files over to SFTP server. Here's the simple steps I follow with Python:
pip install py-pgp
Keep public_key in the same directory
Get recipients info gpg --list-keys
Script:
import os, gnupg
key = '<public_key>.asc'
src = '<file_to_be_encrypted>'
dst = './' #destination, it could be current directory
def encrypt(key, src):
home = os.path.join(os.getcwd(), '')
gpg = gnupg.GPG(gnupghome=home)
with open(key, "rb") as f:
keys = gpg.import_keys(f.read())
with open(src, "rb") as f:
result = gpg.encrypt_file(f, recipients='<name_retrieved_from_public_key>', output='<file_name>.pgp', always_trust=True)
if not result:
raise RuntimeError(result.status)
encrypt(key, src)
This will provide you the Encrypted file within the same directory.