Unable to sign data with X509Certificate in python - c#

I do not have much experiance with security, but now I have to implement a signature procedure in python.
I have a certificate somename.cer. I have an c# implementation example of how to sign my string with that string as follows:
CertColl is the collection of certificates where related code finds the related certificate with Thumbprint in the previous lines and returns a list of certificates.
X509Certificate2 cert = certColl[0]
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
return Convert.ToBase64String(rsa.SignData(Encoding.GetEncoding(1251).GetBytes(my_string), new SHA1CryptoServiceProvider()));
my_string is the string to be signed and constructed within the code, but I do not need to add those steps in here
So I am trying to implement this in Python with the help of this previous Q&A
from Crypto.Util.asn1 import DerSequence
from Crypto.PublicKey import RSA
from binascii import a2b_base64
pem = open("some-path/somefile.cer") # I have a certificate with `cer` extension
lines = pem.replace(" ",'').split()
der = a2b_base64(''.join(lines[1:-1]))
cert = DerSequence()
cert.decode(der)
tbsCertificate = DerSequence()
tbsCertificate.decode(cert[0])
subjectPublicKeyInfo = tbsCertificate[6]
rsa_key = RSA.importKey(subjectPublicKeyInfo)
As I expect, now I can sign my_string with this.
rsa_key.sign("Hello World", "")
But I receive the following error:
TypeError: Private key not available in this object
Am I doing something wrong, like usnig the wrong method to mimic rsa.SignData in python?

Your certificate does not contain the private key.
From what I see in your C# code, I'm guessing you're sourcing the certificate from the Windows Certificate Store. This store can contain certificates both with and without private key attached.
.cer files, on the other hand, (usually) don't contain private keys - they only have public keys. That's why signing with it is impossible.
I'm guessing you have exported the .cer file from the Windows Certificate Store and haven't selected the "Export private key" option. You should have better luck by re-exporting it in .pfx or .pvk format and try signing with that file.
See more on this topic here

Related

.Net Core 3.1 How do I import a .cert and .key file in a X509Certificate2 Object?

This is the first time I am dealing in code with certificates.
My problem is, that I need to sign emails with a certificate that is split in a .cert and in a .key file. Those files need to be read from the file system and cannot be stored in some kind certificate store.
The self signed private key for testing purposes starts like this:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,B5F1CE2CAB1B3CE20326EF3CD60D230
tmPJKtI8S4dGl2B29HhyHlF6Dp6/mDldldX/n2+gYvfSaa4TEPVFQMJfLsRxp1ey
...
Importing the .cert part is fairly easy and straight forward:
X509Certificate2 certificate = new X509Certificate2(_emailConfig.PathToCertificate);
But I fail to figure out how to add the private key which I need for the actual signing.
This needs to also work on Linux.
Any help would be appreciated.
Update 0:
I obtained a string called privateKey that only contains the private key without any PEM syntax.
Then I did the following:
var privateKeyBytes = Convert.FromBase64String(privateKey);
using var rsa = RSA.Create();
rsa.ImportRSAPrivateKey(privateKeyBytes, out _);
Then I assigned the key:
certificate.PrivateKey = rsa;
The problem is I am getting the following Exception: System.Security.Cryptography.CryptographicException : ASN1 corrupted data.

What's the simplest way to distribute an RSA public key with software?

I'm working on some software that exchanges XML documents with a server. The server signs the XML using XMLDSIG and the client should verify the signature before trusting the XML. I'm using RSACryptoServiceProvider to do this. The XML is signed, but not encrypted.
I'm following the basic procedure explained in:
How to Sign XML Documents with Digital Signatures
How to Verify the Digital Signatures of XML Documents
This requires that the client software has the public key available. I want the distribution of the client software to be as simple as possible and I don't want the client to have to deal with certificates. The pair of documents referenced above conveniently skirt around the subject of distributing the public key, simply stating that the user "needs to have the same key". I don't particularly want the end user to even be aware that they have a public key, so asking them to mess around with certificates is out of the question. Since the public key is public, what I would like to do is somehow embed it within the client software. As I see it, my options are:
Install the public key during the setup process
Somehow embed the public key into the software itself, possibly within the App.config file
Is this feasible in practice? What is the simplest way of achieving this that doesn't require any user interaction or awareness?
You don't have to distribute the certificate. One of common approaches is to include the certificate in the signed document, in the KeyInfo/X509Data node.
The validation can use the embedded certificate easily and the only required infrastructure element at the client side is the certificate thumbprint and subject name. In other words, client validates the document using included certificate and then easily checks the certificate agaist the subject name and thumbprint. In case of a match, there is the assumption that a correct certificate has been provided.
Read more about technical details in one of my blog entries (this is a 3 part tutorial so you can also take a look at all other entries). Anyway, no importing certificates and no including certificates with your software, rather you have two string configuration parameters.
The embedded certificate inside the XmlDsigned document has a const size and usually the overhead is neglectable.
http://www.wiktorzychla.com/2012/12/interoperable-xml-digital-signatures-c.html
http://www.wiktorzychla.com/2012/12/interoperable-xml-digital-signatures-c_20.html
Am not sure what problem you're facing without seeing your code but, could this answer from Ji Zhou help?
public static void Main()
{
try
{ //initialze the byte arrays to the public key information.
byte[] PublicKey = {214,46,220,83,160,73,40,39,201,155,19,202,3,11,191,178,56,
74,90,36,248,103,18,144,170,163,145,87,54,61,34,220,222,
207,137,149,173,14,92,120,206,222,158,28,40,24,30,16,175,
108,128,35,230,118,40,121,113,125,216,130,11,24,90,48,194,
240,105,44,76,34,57,249,228,125,80,38,9,136,29,117,207,139,
168,181,85,137,126,10,126,242,120,247,121,8,100,12,201,171,
38,226,193,180,190,117,177,87,143,242,213,11,44,180,113,93,
106,99,179,68,175,211,164,116,64,148,226,254,172,147};
//Values to store encrypted symmetric keys.
byte[] EncryptedSymmetricKey;
byte[] EncryptedSymmetricIV;
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Get an instance of RSAParameters from ExportParameters function.
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = PublicKey;
//Import key parameters into RSA.
RSA.ImportParameters(RSAKeyInfo);
//Create a new instance of the RijndaelManaged class.
RijndaelManaged RM = new RijndaelManaged();
//Encrypt the symmetric key and IV.
EncryptedSymmetricKey = RSA.Encrypt(RM.Key, false);
EncryptedSymmetricIV = RSA.Encrypt(RM.IV, false);
Console.WriteLine("RijndaelManaged Key and IV have been encrypted with RSACryptoServiceProvider.");
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
}
}

Sign Xml with digital certificate in format of PKCS#7 in DER (ITU-T Rec. X.690)

I have a .xml file that has to be signed with digital certificate in format of PKCS#7 version 1.5 (RFC 2315) and DER (ITU-T Recommendation X.690
That .xml will be send to a govt. WebService that only accept the format I mentioned upwards.
What I'm able to do - thanks to this website is digitaly sign .xml with the .pfx file that I generated with Certificate Export Wizard explained below. The class that I'm using to sign is down on mentioned website or here.
From what I tried to understand so far I will need to sign the .xml with .pfx file according to X.690 standards but I'm only able to access this namespace:
using System.Security.Cryptography.X509Certificates;
which is obviously for X.509 format.
Note:
There are several things I'm confused about - to export the certificate into .pfx I'm using Internet Explorer - Certificate Export Wizard from there I'm able to:
Yes - export private keys - then it will be generated in PKCS#12 but .pfx
No - do not export private keys - Certificate according to standards Cryptographic Message Syntax Standard - PKCS#7 that I guess I need but I would receive .p7b file
I must say that I am a newbie in certificates and digital signatures so I'm not even sure if I'm correctly exporting the certificat and the second thing is how I can sign according to X.690 standards.
May I know how to sign according to X.690 format please?
Thank you everyone for your time and replies.
My code is following:
bool res = false;
try
{
byte[] certBytes = System.IO.File.ReadAllBytes(pvkPath);
X509Certificate2 cert = new X509Certificate2(certBytes, certPass);
XmlDocument doc = new XmlDocument();
doc.Load(xmlPath);
// sign the document with the private key of the certificate
XmlDsig.SignXml(doc, cert);
// save the document
doc.Save(xmlSavePath);
// verify that the document has a signature
bool hasSignature = XmlDsig.HasSignatureElement(doc);
return res = true;
}
catch (Exception)
{ return res; }
foDigital signature in PKCS#7/CMS format is blob that contains your XML data + signer's x509 public key certificate (.cer file) + Digital signature. The entire blob is encoded in ASN 1.0 format(X690). There may be variations in the blob due to the absence of original data or the signer certificate, This variation is called detached signatures.
Digital signature is generated when you sign your xml file with the signer's private key. This signature can be verified when you send your XML file + signer's public key (as X509 .cer file)+ digital signature to the party who are interested in verifying it.
PFX/p12 is a container that contains both the signer's private key and public key. You get this key pair from either your government or your government approved key custodians. You will then use this PFX to perform digital signature.
PKCS#7 is supported by cryptoAPI.
The above are the basics. This should allow you make your queries more clearly.

How can I import private key and use it to sign document in C#?

I've generate a self-signed certificates(X509 certificate) and want to use the private key to sign some document to make digital signature and I'm doing it in C#.
How can I import .pvk file? Do I need to import from key store?
And can I use this code to sign and create a digital signature?
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
RSAFormatter.SetHashAlgorithm("SHA1");
RSAFormatter.SetKey(cert.PrivateKey);
byte[] SignedHash = RSAFormatter.CreateSignature(data);
Try tho Initialize your RSA like this:
RSACryptoServiceProvider RSA = (RSACryptoServiceProvider)cert.PrivateKey;
To generate a signature I use rsa.SignData(data, "sha1");
First of all you need to determine, in what format the signature should be made. If you deal with certificates, most likely you will create PKCS#7 or CMS signature. There's a class in .NET for CMS signatures.
The next steps depend on whether you need certificate-based PKCS#7 signature or RSA (PKCS#1) signature.
Side note - putting several questions into one makes it hard to answer properly. Also most of your questions have lots of answers if you do the search.

How to validate X.509 Certificate in C# using Compact Framework

I am trying to validate an X.509 certificate using C# and .NetCF. I have the CA certificate, and if I understand correctly, I need to use the public key from this CA certificate to decrypt the signature of the untrusted certificate. This should give me the computed hash value of the untrusted certificate. I should then compute the hash of the certificate myself and make sure the two values match.
I've been playing with this for a few days and I'm not getting very far. I've been using the X509Certificate and RSACryptoServiceProvider classes. First, I tried to get the public key and signature out of the X509Certificate class. I was able to get the public key but not the signature. Next, I tried parsing the binary data that made up the certificate, which allowed me to get the signature (and any other data I wanted), but I was unable to decrypt the signature using the RSACryptoServiceProvider. I tried things like this but kept getting exceptions saying "Bad Key" when I tried to decrypt:
RSAParameters rsaParams = new RSAParameters();
rsaParams.Exponent = exp;
rsaParams.Modulus = mod;
RSACryptoServiceProvider rsaServ = new RSACryptoServiceProvider();
rsaServ.ImportParameters(rsaParams);
byte[] decryptedSig = rsaServ.Decrypt(encryptedSig, false);
Any advice would be greatly appreciated.
Edit:
I tried something that seems to be better but is returning a strange result. I'm working with the X509Certificate2 class here because it's a little easier for testing, but I will need to switch to X509Certificate for .NetCF later. I think that RSACryptoServiceProvider.VerifyData might be what I need. I tried the following code.
X509Certificate2 cert = new X509Certificate2(certBytes);
X509Certificate2 certCA1 = new X509Certificate2(#"C:\certs\certCA1.cer");
byte[] encryptedSig = new byte[256];
Array.Copy(certBytes, certBytes.Length - 256, encryptedSig, 0, 256);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certA1.PublicKey.Key;
bool good = rsa.VerifyData(cert.RawData, "1.3.14.3.2.26", encryptedSig);
As I said, I am able to manually decode and interpret the binary data of the certificate, so I'm pretty sure the cert.RawData is the certificate's signed data and the last 256 bytes are the encrypted signature. The string is the OID of the hash algorithm, which I got from certificate, but I'm not 100% sure that it's correct. VerifyData returns false, but I'm not sure why yet.
Thoughts?
Here is my code.
RSACryptoServiceProvider rsa = signingCertificate_GetPublicKey();
return rsa.VerifyData( SignedValue(), CryptoConfig.MapNameToOID( "SHA1" ), Signature() );
RSACryptoServiceProvider signingCertificate_GetPublicKey()
{
RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
RSAParameters publicKeyParams = new RSAParameters();
publicKeyParams.Modulus = GetPublicKeyModulus();
publicKeyParams.Exponent = GetPublicKeyExponent();
publicKey.ImportParameters( publicKeyParams );
return publicKey;
}
byte[] GetPublicKeyExponent()
{
// The value of the second TLV in your Public Key
}
byte[] GetPublicKeyModulus()
{
// The value of the first TLV in your Public Key
}
byte[] SignedValue()
{
// The first TLV in your Ceritificate
}
byte[] Signature()
{
// The value of the third TLV in your Certificate
}
I hope that helps anyone who is working on this problem.
Does WinCE support something compatible with the Win32 MSCrypto.dll? If yes, take a look at the .NET X509Certificate2 class and also the CLR Security library on Codeplex. It contains a lot of helpful routines for .NET that sit on top of the core OS crypto library. You can download the source and see how it compiles for .NetCF
To load and validate an X509 certificate, do something like this (untested):
var cert = new X509Certificate2("mycert.cer");
if (!cert.Verify())
{
<fail>
}
There are nearly a dozen constructors for X509Certificate2 to construct from a wide variety of sources - file on disk, byte array in memory, load from local cert store, etc.
The root CA used to sign the certificate will need to be installed in the local cert store. If the certificate does not include the intermediate CAs in the trust chain, those intermediates will need to be on the local machine too, all the way up to a root CA that is in the trusted cert store in the local machine.
Unfortunately, I can't tell from the MSDN docs whether X509Certificate2 is available on .NetCF.
It works for me in win32, but in Compact Framework I'm facing the same problem, there is no X509Certificate2 so I'm actually blocked, with win32 we can do:
X509Certificate2 l__PublicKeyCertificate = new X509Certificate2("cert.cer");
RSACryptoServiceProvider l__rsaCspPublic = (RSACryptoServiceProvider)l__PublicKeyCertificate .PublicKey.Key;
//...
l__isVerified = l__rsaCspPublic.VerifyData(l__fileData, CryptoConfig.MapNameToOID("SHA1"), l__fileSignature);

Categories

Resources