How to create certificate object from public key in PEM format? - c#

I want to use iText to embed signed hash and public in PDF.
As per arguments to sign method in iText 7 I need to pass certificate chain,
How can I create this certificate object directly from public key string?
Update 1
Below is small c# code. You can see I am trying to get x509 certificate from public key. This certificate will be used to verify the signed data from corresponding private key. Also it will be used to embed this public certificate and signed hash into PDF for digital signature.
In below code I am getting error as below
Error:
'DigiSignDemo.exe' (CLR v4.0.30319: DigiSignDemo.exe): Loaded 'C:\Users\xposs\source\repos\DigiSignDemo\bin\Debug\itext.forms.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Exception thrown: 'System.Security.Cryptography.CryptographicException' in mscorlib.dll
An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Cannot find the requested object.
public static readonly string publickey = #"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGhYfAvWxqIwsZsO1zUN
NyFT/3US7HGLXiW48NvYn2qNyn/9hm/BFWG901YoJAjlPTcNtMo1t8lUr2dRkc3l
8YyP8SetWKbznerQuXYBZZy31kp8u3Wj+zQSroZsFn69FoMAMWXqhkw9woFumINe
gw4sMtQ1S8CucX0uXJ4a2ElzoaUKp1M+MOCATDnmsXSyf/2/ERO71SpD+alDV2rE
m5DqvEnE0t27fm7PpNeCX0XEHRvx620LooGv1Co+0w5Au37sfSjOZp1B9V0n8KFR
6gLFY7mAZ1krZJscYgkNAPIz2QE6voBR8OVSHMnNcPH+0KLfGuNVHhaTyI4naPH+
0QIDAQAB
-----END PUBLIC KEY-----
";
public static System.Security.Cryptography.X509Certificates.X509Certificate getPublicCertificate()
{
//Here below I am getting error
X509Certificate2 clientCertificate =
new X509Certificate2(Encoding.UTF8.GetBytes(publickey));
return clientCertificate;
}

You can't create a certificate from a public key. It's analogous to asking how to create a car from a steering wheel... you're missing a lot of other stuff before it'd be a car.
Given that you have an RSA public key in the SubjectPublicKeyInfo format, you can import it as an RSA key, starting with .NET Core 3.0, via
RSA rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(
Convert.FromBase64String(#"
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGhYfAvWxqIwsZsO1zUN
NyFT/3US7HGLXiW48NvYn2qNyn/9hm/BFWG901YoJAjlPTcNtMo1t8lUr2dRkc3l
8YyP8SetWKbznerQuXYBZZy31kp8u3Wj+zQSroZsFn69FoMAMWXqhkw9woFumINe
gw4sMtQ1S8CucX0uXJ4a2ElzoaUKp1M+MOCATDnmsXSyf/2/ERO71SpD+alDV2rE
m5DqvEnE0t27fm7PpNeCX0XEHRvx620LooGv1Co+0w5Au37sfSjOZp1B9V0n8KFR
6gLFY7mAZ1krZJscYgkNAPIz2QE6voBR8OVSHMnNcPH+0KLfGuNVHhaTyI4naPH+
0QIDAQAB"),
out _);
// the key is loaded now.
If you're not on .NET Core, this is a lot harder. See How to load the RSA public key from file in C# or How to get RSACryptoServiceProvider public and private key only in c# for more information.

#bartonjs in his answer already has mentioned that a X509 certificate is much more than a public key. Here comes an overwiew to show how much more.
For this let's look at a specification of X509 certificates, RFC 5280. The certificate structure is specified in ASN.1 which is quite easy to follow even if one does not know it yet.
Here you'll see that first of all a X509 certificate is something signed:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
The private key used for this signature may again be associated with the public key of another certificate (issuer certificate) which again may be signed using a private key associated with the public key of yet another certificate. This gives rise to a chain of certificates which usually ends in a certificate which is signed using the private key of this certificate itself, a self-signed certificate.
To verify whether one trusts the information in a certificate, one usually follows its issuer chain and checks whether one eventually gets to some certificate which is from a small collection of certificates one trusts explicitly.
The TBSCertificate in it contains the information to-be-signed. It is specified as
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] EXPLICIT Extensions OPTIONAL
}
Here you in particular a subject Name, an issue Name and a serialNumber. The subject name describes the holder of this certificate, the issue name is the subject name of the issuer certificate, and the serial number is a unique number determined by the issuer when signing the certificate.
The validity interval indicates the intended time span of validity of the certificate.
Then there eventually is the SubjectPublicKeyInfo which contains the public key of the certificate.
Finally the extensions are an optional collection containing extra information like the allowed usages of the certificate (and its private key) and URLs where revocation information for the certificate can be retrieved.
As you can see you cannot simply the certificate based on the public key alone. You can create a certificate based thereon alone but don't expect anyone to trust it.

Related

Understanding self-signed certificates in c#

Recently I came across this c# code:
var dn = new X500DistinguishedName($"CN={_appSettings.CommonName};OU={_appSettings.OrganizationalUnit}", X500DistinguishedNameFlags.UseSemicolons);
SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddUri(new Uri($"urn:{_appSettings.ApplicationUri}"));
using (RSA rsa = RSA.Create(2048))
{
var request = new CertificateRequest(dn, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(sanBuilder.Build());
var selfSignedCert = request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
...
}
...
Having a look closer at the CertificateRequest constructor parameters, the rsa key is described as:
A RSA key whose public key material will be included in the certificate or certificate request. If the CreateSelfSigned(DateTimeOffset, DateTimeOffset) method is called, this key is used as a private key.
The bold part is the one I don't really understand. Does that mean that when self signing the certificate, the certificate is signed using the given RSA key AND adds the same key as public key to the certificate?
In my understanding for TLS, we have two public-key pairs, one for signing and one for encryption. The CA signs a certificate with its private key and offers a public key to the clients to verify the signature by decrypting it with the public key, whereas the provider of a service offers a public key which the clients use to encrypt their keys first in the tls handshake which after that gets decrypted with the service providers private key.
However, in the above code sample, we create a certificate that contains what exactly? Server public key is for encryption, but what key for decryption of the signature?

Using public key in asymmetric encryption with X.509 certificates

Sometimes ago I've read an article about using asymmetric keys like public and private keys to send data securely. What I've understood was that the server has 1 key (private key) that it use to encrypt data and all clients use the second key (public key) to decrypt it.
Now how should I expect to receive the key and how should I work with?
If I receive a Certificate from the server, wouldn't it contain both public and private keys?!
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
var cert = GetCertificate(certStore);
var privatekey= cert.PrivateKey;
var publicKey= cert.PublicKey;
Is it possbile to remove the private key from the certificate? How? and how can I understand if the certificate has the private key?
First a little bit of clarification:
In Public-key cryptography the public key is used to encrypt the data and the private key is used (by the server) to decrypt the data.
The owner of the private key stores private key and only shares a public key.
Any certificate from a server should contain only a public key.
(It would be a big security issue if the certificate contains the private Key. You could decrypt the messages from any other user)
To check if the cerificate has a private key you can use the HasPrivateKey-Property
cert.HasPrivateKey;
And to get a certificate with only the public key you can use:
byte[] bytes = cert.Export(X509ContentType.Cert);
var publicCert = new X509Certificate2(bytes);
If I receive a Certificate from the server, wouldn't it contain both public and private keys?!
No, not necessarily.
There are two different ways of obtaining the certificate
PKCS#7/PEM - the file usually contains only the public part of the certificate
PKCS#12/PFX - the store contains certificates with private keys
Is it possbile to remove the private key from the certificate?
By exporting it to a format that lets you store only the public part of the certificate.
Open a web browser, navigate to any site that uses SSL.
Now click the lock icon and, from there, the certificate information. What you have at the client side (in the browser) is the certificate without the private key. You can save it and even import into the system cert store but still without the private key.
and how can I understand if the certificate has the private key?
If you load it with your C# code, accessing the HasPrivateKey property will be true only for certs with private keys available.

How to work with (and create) X509 Certificates for private/public key encryption of JWT Tokens

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.

How can I check signature of a SignedCms envelope?

I don't really understand how to work with PKCS#7 messages.
I sign some byte array with a X509Certificate2 I have and get also a byte array.
byte[] data = new byte[5] { 110, 111, 112, 113, 114 }, signedData;
X509Certificate2 cert = new X509Certificate2(certPath, password);
ContentInfo content = new ContentInfo(data);
SignedCms envelope = new SignedCms(content);
CmsSigner cmsSigner = new CmsSigner(cert);
envelope.ComputeSignature(cmsSigner);
signedData = envelope.Encode();
The signedData is transmitted to some remote recipient and he gets the SignedCms envelope.
SignedCms envelope = new SignedCms();
envelope.Decode(signedData);
How can he decode the envelope? He doesn't pass my public key as a parameter. There's my public key in the envelope, in SignerInfo property, but is there any reason for that, cause anyone can replace it with the whole signature?
He can the recipient make sure, using my public key that he has, that the actual sender of the envelope is me?
There's method envelope.CheckSignature(new X509Certificate2Collection(certificate), true); but I tried to use wrong certificate and there was no exception thrown.
A PKCS#7 / CMS / S/MIME signed message is a data container which has (in addition to some other metadata):
EncapsulatedContentInfo
ContentInfoType
EncapsulatedContent (the message bytes)
Certificates (Optional)
CRLs (Optional)
SignerInfos
DigestAlgorithm (e.g. SHA-1)
SignedAttributes (Optional, allows other context information to be signed)
SignatureAlgorithm (e.g. RSA, DSA, ECDSA)
SignatureValue (the signature bytes)
UnsignedAttributes (Optional, allows for after-signing information, like counter-signatures)
(This is a summary of RFC 2630 (Cryptographic Message Syntax) Section 5)
SignedCms.Decode reads the encoded message and populates members. Every direct signatory to the message can be read from the SignedCms::SignerInfos property (counter-signers, or entities which have signed that they witnessed the original signature, can be read from SignerInfo::CounterSignerInfos).
When you call SignedCms.CheckSignature, it checks every SigerInfo and verifies that the signature can be successfully verified (or throws an exception), as well as that every counter-signer signature can be worked out.
What it doesn't know is that any of the signers "made sense". For that check you would need to loop over each SignerInfo and look at (for example) the Certificate property; then perform a suitability check:
Perhaps it is a pre-registered public key
Perhaps it chains up to a well-known root or intermediate CA
Perhaps it has some sort of Extension which shows it to be suitable
This part SignedCms cannot realistically do for you, since there's no default notion of "suitable" for messages, unlike the hostname verification of TLS.
If you want to assess the signature of a single signer, you can call SignedInfo::CheckSignature, but that's redundant if you also called SignedCms::CheckSignature.
There's method envelope.CheckSignature(new X509Certificate2Collection(certificate), true); but I tried to use wrong certificate and there was no exception thrown.
The extraCerts overloads provide extra certificates. It's valid to have a SignedCms message which does not embed the signer certificates, leaving it up to the recipient to have known the valid certs ahead of time (e.g. using a per-user database of pre-registered certificates). You didn't get an exception because the correct certificates were found within the provided certificates collection.
You can see what was in the provided certificates collection via the X509Certificate2Collection.Import methods; they can read a PKCS#7 signed-data message and populate the collection with the optional embedded certificates.
A PKCS#7 by itself is just a signature, could it be replaced? sure. envelope.CheckSiganture just validates that pkcs#7 has the right format and length, in other words checks if a pkcs#7 is well constructed.
Broadly putted, you need to implement a PKI (Private Key Infrastructure). Where in one end you construct your pkcs#7 using a public key, and on the other end you must validate that the pkcs#7 you have actually has a valid certificate that you recognize as your own. You must implement an OCSP to validate those certificates and if everything checks out all right you should and must request a timestamp to a third party to vouch for your pkcs#7. Also you will need a vault (database) to keep track of everything: pkcs#7's, data hashes, timestamps, original data, ocsp responses...
But if you are only interested in knowing how to identify a pkcs#7, there are various tools you could use to decode a PKCS#7, this action gives back all the information contained in it. Or you could create your own using c#.

Encryption with PGP key

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.

Categories

Resources