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?
Related
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.
Need an assist on an issue I am having with an RSA key. I generate a RSA key through C# code on a server with a key container name I define such as "CustKey". I get back the key XML string just fine and I can connect an application to it using that key. I have an API that is generating the key through an endpoint. If I redeploy the API my application that uses the public key stops working on decoding it. I have to run again the same code to create the key and the encrypted string is exactly the same every time I regenerate it and then the consuming app works fine again. It seems to happen when either the app pool recycles but I have it turned off recycling but the publish I stop and start the IIS API site. Any idea how to keep the key working and why I have to regenerate it but it uses the same key encrypted value each time.
Thanks for any info.
Some examples of your code would greatly help to understand what's going on. I can only speculate at this point that you're trying to decode your message with public key instead of private one.
Firstly, you have to have two paired keys:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string publicKey = rsa.ToXmlString(false);
string privateKey = rsa.ToXmlString(true);
Secondly, you have to give your public key to all people who wants to send a secret message to you:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(publicKey);
string secretMessage = Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes(openMessage), false));
And finally, when you recieve a secret message only you can decode it because only you have a private key which is paired to public one:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(privateKey);
string revealedMessage = Encoding.UTF8.GetString(rsa.Decrypt(Convert.FromBase64String(secretMessage), false));
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.
I've been struggeling with this for days now and RFC 2315 is a bit hard to understand.
I'm trying to implement my own version of EnvelopedCms.Decrypt(), so that I can use the certificate operations of Azure Key Vault to UnwrapKey and/or Decrypt a PKCS#7 message (CMS Object) in a correct way. I use EnevelopedCms in .Net to Decode the message, then I try to Decrypt the EnvelopedCms.ContentInfo.Content.
This is what I try to do;
public static async Task<byte[]> DecryptCustom(string certificateId, string encryptedBase64Content)
{
var bytes = Convert.FromBase64String(encryptedBase64Content);
var contentInfo = new ContentInfo(bytes);
var envelopedCms = new EnvelopedCms(contentInfo);
envelopedCms.Decode(bytes);
// envelopedCms.Decrypt() <-- no go. Can't extract certificate from Key Vault
// My (naive) attempt to decrypt CMS content using Azure Key Vault certificates
byte[] decryptedContent;
using (var client = new KeyVaultClient(GetKeyVaultToken))
{
var decryptionresult = await client.DecryptAsync(GetKeyUrl(certificateId), "RSA1_5", envelopedCms.ContentInfo.Content);
decryptedContent = decryptionresult.Result;
}
return decryptedContent;
}
I was hoping it could be that easy, but it gives me the following error;
Unable to decrypt specified value with this key.
I read something about octets in RFC 2315, so maybe the stream (byte-array) needs some reordering before I decrypt. Do I need to unwrap some symmetric key to decrypt the real payload? I'm on thin ice here.
I'm not a cryptography professional so I might have missed something obvious, too. I was hoping someone knew what to do in this case as I really want to keep my certificates inside the Key Vault (HSM)
CMS envelope contents are encrypted using a session key, and this key is encrypted with each recipients (there can be many) public key before transmission.
What you need is to extract your recipient's encrypted session key, and unwrap it with the private key stored in key vault. I'm not near Visual Studio right now, but here is the pseudocode:
// Extract the first (and often only) receiver's encrypted session key
var key = envelopedCms.Receivers[0].EncryptionKey;
// Unwrap the sessionKey using the receiver's private key stored in key vault:
var sessionKey = (await keyVaultClient.Unwrap(uri, "certificatename", key)).Result;
Finally, using the sessionKey, you can decrypt the envelope contents (ContentInfo.Content). The encryption type is specified in the envelope's encryption algorithm-property.
So, I am creating a simple "web chat", using TcpClient and TcpListener class. I want all data sent to be encrpyted, and I am using AES encryption. So first I have to make sure AES key from server is securely sent to client. I am trying to achieve this, by encrypting AES key with RSA and then sending it to client and there decrypting it with RSA again.
So first of all I created an RSACryptoServiceProvider on server and extracted public key. I sent public key to client and there created RSACryptoServiceProvider and imported that key. When I call Decrpyt method I get an key does not exist exception. This is my code:
Server:
This is sending public key to client.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string privateXml = rsa.ToXmlString(true);
string publicXml = rsa.ToXmlString(false);
Byte[] pubKey = Encoding.UTF8.GetBytes(publicXml);
clientStream.Write(pubKey, 0, pubKey.Length);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); // simetrično kriptiranje
byte[] aesKey = aes.Key;
byte[] encryptedRSA = rsa.Encrypt(aesKey, false);
clientStream.Write(encryptedRSA, 0, encryptedRSA.Length);
Client:
Byte[] serverPublicKey = new Byte[1024];
Int32 bytes1 = stream.Read(serverPublicKey, 0, serverPublicKey.Length);
string serverKey = Encoding.UTF8.GetString(serverPublicKey, 0, bytes1);
serverKey = serverKey.Replace("\0", "");
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(serverKey);
Byte[] bytes2 = new Byte[128];
String aesKey = null;
stream.Read(bytes2, 0, bytes2.Length);
byte[] decryptedKey = rsa.Decrypt(bytes2, false);
Sorry this wasn't small enough to fit in a comment.
You've sent the public key to the client. This will only allow the client to encrypt data to be sent to the server. To decrypt data the client would need a private key (hence your exception)
Sending your public key to someone does not allow you to send them encrypted messages, it allows them to send you encrypted messages securely, so in your example only the client can send an encrypted message.
In your scenario this would mean that the client would need to generate an AES key, encrypt it using the public key it has been been sent and then the server can decrypt it and use the AES key. HOWEVER I would not recommend this as it has many flaws including being very susceptible to a man in the middle attack. This is because we have no way to verify the public key we receive is the one that belongs to the server (some one else could be intercepting and modifying the tcp stream to be inserting their own key pair and thereby gaining access to the AES key and being able to snoop on the rest of the communication).
You should consider looking into using the SslStream class http://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.100).aspx
If you wanted to carry on as you have been then you would need to let the client generate the key and have some mechanism to verify the public key received.
The usually way of verifying public keys is by using certificates (i.e. You have a third party (certificate authority) that both the server and client trusts and that third party has signed the public key to say it does actually belong to the server)
If you don't want to get a certificate that is signed by a trusted certificate authority then you could use a self signed certificate but there is not much benefit over just hardcoding the public key into the client application as you would have to hardcode the certificate thumbprint of the self signed certificate anyway.