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.
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);
}
}
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
I have seen question on signing and encrypting final mdm profile here:
iOS MDM profile signing, which certificate to use?
I am using Bouncy Castle library for encryption. Currently I am stuck while encrypting the final profile using the scep identitiy certificate.
I am facing the following issue.
The public key retrieved from with scep response certificate is not 16byte(128 bit) so encryption is failing with a message Key should be 128 bit.
If I can change the public key to 16byte using the following code the device throws invalid profile dailog.
public static string getKeyMessageDigest(string key)
{
byte[] ByteData = Encoding.UTF8.GetBytes(key);
//MD5 creating MD5 object.
MD5 oMd5 = MD5.Create();
byte[] HashData = oMd5.ComputeHash(ByteData);
//convert byte array to hex format
StringBuilder oSb = new StringBuilder();
for (int x = 0; x < HashData.Length; x++)
{
//hexadecimal string value
oSb.Append(HashData[x].ToString("x2"));
}
return Convert.ToString(oSb);
}
Can some one help me with some blog or sample code to encrypt the profile? Appreciate your help.
I had a similar problem. PFB the working code that I'm using to encrypt now. I'm retrieving the signing certificate from the device response, retrieving the public key from it and using the same to encrypt.
byte[] request = StreamToByte(ResponseFromDevice);
var signer = new SignedCms();
signer.Decode(request);
X509Certificate2 certificate = signer.Certificates[0];
string xmlData = "payload string to encrypt";
Byte[] cleartextsbyte = UTF8Encoding.UTF8.GetBytes(xmlData);
ContentInfo contentinfo = new ContentInfo(cleartextsbyte);
EnvelopedCms envelopedCms = new EnvelopedCms(contentinfo);
CmsRecipient recipient = new CmsRecipient(certificate);
envelopedCms.Encrypt(recipient);
string data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><dict><key>EncryptedPayloadContent</key><data>[ENCRYPTEDDATA]</data><key>PayloadDescription</key><string>For profile enrollment</string><key>PayloadDisplayName</key><string>ProfileName</string><key>PayloadIdentifier</key><string>YourIdentifier</string><key>PayloadOrganization</key><string>YourOrg</string><key>PayloadRemovalDisallowed</key><false/><key>PayloadType</key><string>Configuration</string><key>PayloadUUID</key><string>YourUDID/string><key>PayloadVersion</key><integer>1</integer></dict></plist>";
data = data.Replace("[ENCRYPTEDDATA]", Convert.ToBase64String(envelopedCms.Encode()));
HttpContext.Current.Response.Write(data);
WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-apple-aspen-config";
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;
I answered in comments on your previous question:
"I would recommend to take a look on OS X Server MDM implementation.
Generally speaking to encrypt profile, as I remember you should use PKCS7 wrapping. So, you should look at this: http://www.cs.berkeley.edu/~jonah/bc/org/bouncycastle/jce/PKCS7SignedData.html
BTW. I would recommend to read up a little bit on cryptography, if you want to get general understanding. Very-very high level overview of your problem: you are trying to use RSA key directly to encrypt the data. However, it should be used to encrypt a symmetric key which in its turn is used to encrypt the data."
You can also take a look here:
PKCS#7 Encryption
Your code won't work, because it's
- not PKCS7
- you are trying to use MD5(public certificate key) which doesn't make any sense
I would really-really recommend to read again MDM documentation and something on cryptopraphy. It's quite easy to make it wrong (both non working or unsecure implementation).
In bouncycastle you have to encrypt it using CMSAlgorithm.DES_EDE3_CBC. Then signed the data as you done in the previous step. Make sure you Base64 encode the encrypted payload before signing.
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.