PFX Self Signed x509 Certificate with password secure - c#

After looking at how to generate self-signed digital signatures from Creating a self-signed certificate in C#, I can call CreateSelfSignCertificatePfx and get PXF data in a byte array back, which can then be used within an X509Certificate2 object to sign and verify. Example...
byte[] pfx = Certificate.CreateSelfSignCertificatePfx("O=Company,CN=Firstname,SN=Lastname", DateTime.Now, DateTime.Now.AddYears(1), "password");
X509Certificate2 cert = new X509Certificate2(pfx, "password");
byte[] publicBytes = cert.RawData;
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
byte[] signedData = rsa.SignData(new System.Text.UTF8Encoding().GetBytes("Test"), new SHA1CryptoServiceProvider());
RSACryptoServiceProvider rsa2 = (RSACryptoServiceProvider)new X509Certificate2(publicBytes).PublicKey.Key;
bool verified = rsa2.VerifyData(new System.Text.UTF8Encoding().GetBytes("Test"), new SHA1CryptoServiceProvider(), signedData);
This works. My concern though is the original bytes, byte[] pfx from above, need to be stored in a DB (to sign stuff). The question becomes, how secure are the bytes in this format? I know you need the password to construct the new X509Certificate2 with a private key, but in a general sense, how secure are the bytes without the password? I have no problems encrypting these bytes as an added layer, but is that necessary?
According to X509Certificate2.X509Certificate2(Byte[], String) Constructor
Calling this constructor with the correct password decrypts the private key and saves it to a key container.
I just want to ensure the private key is safe without the password.

In my eyes the question is not whether you should put the "bytes" in the database, but more, would you put the file with the private key in your file system.
In the way you're doing it, it's essentially the same thing. You're just storing the bytes that make up the cert file.
I may be failing to understand the difference here, but they bytes and the file are essentially the same thing, the only difference being the fact that one has to gain access to the db to get them.

Use a smartcard or token to store your private key.
UPDATE:
The Pvt key can be accessed by anyone who can access the machine.

The private keys in a PFX (PKCS#12) are stored encrypted, which is of course what the password is for. Not all of a PFX is encrypted, the structural pieces stay plaintext to contain metadata about the contents (like what encryption algorithm was used).
Based on inspecting the file, as of Windows 7 the private keys are encrypted using 3-key (168-bit) 3DES. The key is derived via a complex formula involving your password; there's nothing saved in the file which gives any indication as to what your password was, how long it was, et cetera.
The password is usually proven correct by the addition of a MAC on the contents, which uses the same password for its key derivation function. In the possible case of the MAC password and the encryption password being different (which I've personally never seen) the password is verified by the structural information in the encrypted payload.
DES' weakness mainly lay in the small keysize, it's easily brute forcable today. A 3-key 3DES key has 112 more semantic bits than a (1)DES key, making it take 2^112 (~5 x 10^33) times longer to break.
So, at the end of the day, the private key is cryptographically sound. But like anything with a password-based input, if you use a bad password that is easily guessed then it can be cracked by brute force.

Related

how to use p7b and p12 certificates

Hello my goal is it to encrypt the password of a zipfile. As for now I only had to generate a SHA Hash which will be my password and used the RSA-Cryptoprovider with me private key to encrypt the password.
Now I need to use .p7b and .p12 certificates to do this task.
But I dont understand what do I need those files for after some research I figured out that .p12 will be probably the file that I use to encrypt my password but what do I need .p7b for?
That is what I did so far with it, it seems to work because I can read the string but still what do I need my .p7b file for?
var password = #"test";
var p12FilePath = #"key\Test.p12";
var text = #"myFutureZipPassword";
X509Certificate2 cert = new X509Certificate2(p12FilePath, password, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
byte[] certData = cert.Export(X509ContentType.Pfx, password);
RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)cert.PrivateKey;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
var sign = rsaKey.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
var str = System.Text.Encoding.Default.GetString(sign);
Neither p12 - the extension for PKCS#12 key/certificate stores - nor .p7b - the extension for PKCS#7 binary Cryptographic Message Syntax - is a certificate.
PKCS#12 can be used to store multiple private key / certificate chains (required to sign or decrypt) or just trusted certificates (used to verify or encrypt). Other information can be stored as well, but for your assignment you should just use it as key / certificate store.
The PKCS#7 standard specifies the Cryptographic Message Syntax or CMS. It specifies a container format which can be used to specify signed or enveloped (read: encrypted) messages / files. Whereas RSA by itself can encrypt small files, PKCS#7 allows you to use a well specified form of hybrid encryption.
The container format indicates and may contain the certificates used to perform the encryption so you can choose which key to use. It also specifies the algorithms used to perform the encryption. To use it you need a higher level API than RSACryptoServiceProvider; it is provided in the PKCS namespace within the .NET framework (start with EnvelopedCms). PKCS stands for Public Key Cryptography Standards, which were first created by RSA Labs and then copied and continued in RFC's.
The structure of PKCS#12 and PKCS#7 have been defined in a language that defines data structures called ASN.1. The structures are then encoded using a binary encoding format called BER / DER. However, to make sure that the messages are correctly transmitted over a text based interface, the PKCS#7 file is often ASCII armored using PEM text encoding, which is just basically a few header / footer lines with base 64 encoded binary in between. The b is there in the filename to show that the PEM encoding should not be performed.
Note that sometimes the PKCS#7 / CMS is also used to simply store certificates, which could be used for encryption as you only need the public key for that. However, it is much nicer to use PKCS#12 for that.

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);
}
}

input too large for RSA cipher with BouncyCastle

I'm trying to write a service to SFTP to a server on a given interval, download all files in a directory, and then decrypt them for processing.
The decrypt method was working at one point, and I have no modified it since. I am still using the same keys as when it did successfully work.
I am using the PGPEncrypt, PGPDecrypt, and PGPEncryptionKeys classes for BouncyCastle found here: https://github.com/sledwith/PGP-Decryption-With-C-Sharp
Additionally, I have modified the code as shown here: Exception on decrypting file using BouncyCastle PGP
If you noticed, I commented on how that fixed my code and the decryption worked.
Now, it isn't.
Does anyone have some insight why this might have happened, or how I could go about fixing it?
I am going to make a guess that you are using a 1024 bit RSA public/private key and trying to encrypt 128 bytes of something with it? If I am right, use a larger RSA key (RSA 4096 will allow you to encrypt up to ~500 bytes).
I note in the linked post you say this is sporadic. Some googling indicates that if you have leading zeros in the to-be-encrypted bytes it may be able to handle a full 128 bytes.
Also - if you are just encrypting "data" with the keypair, you need to be certain it will not overrun these limitations (240 or so bytes for RSA 2048 and 500 or so for RSA 4096 accounting for padding). Good practice and future proofing would be to implement a symmetric key encryption of all your data and just use the RSA key to encrypt / decrypt your keys.
If you're not dead-set on using the PGP process explicitly, you might use my library here:
https://github.com/zenith-nz/ObscurCore
Its "banner feature" is not yet fully active (creating encrypted archives of a kind), but it does everything that you want it for, it appears - it does key derivation with a ECDHC scheme, UM1, and implements a large variety of block and stream ciphers. It's built on top of BouncyCastle.
I think I resolved this problem; please try this code.
public string StringToDecrypt(string text)
{
byte[] toDecrypt = Convert.FromBase64String(text);
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(#"Private Key File Path"))
{
keyPair = (AsymmetricCipherKeyPair) new PemReader(reader).ReadObject();
}
var engine = new RsaEngine();
engine.Init(false, keyPair.Private);
return Encoding.UTF8.GetString(engine.ProcessBlock(toDecrypt, 0, toDecrypt.Length));
}

C# RSA Encrypt Private Decrypt Public?

Here is the problem I am trying to solve. Let's say I was releasing some web software built on ASP.NET MVC and I only wanted the end user to be able to add X users to the system. The software would be hosted on their servers.
I want to include an encrypted file that when the user tries to add a new user, it goes out and reads from the file an encrypted string. When the website decodes it, the clear text will be the number of allowed users.
What is the best/simplest way on my end to encrypt to generate this string on my end then decode it back to clear text in my application? Obviously I want to ensure that the end user cannot be spinning up their own encrypted string and just replace mine. I don't want to worry about having to try and obfuscate my source so that they would not be able to see how I decode the string.
Is it possible to encrypt with a private rsa key, then decrypt it with the public one? I haven't had luck with that in the code below:
var rsa = new RSACryptoServiceProvider();
var pubicKey = rsa.ToXmlString(false);
var privateKey = rsa.ToXmlString(true);
var test = "this string needs to be encrypted then decrypted";
var rsa2 = new RSACryptoServiceProvider();
rsa2.FromXmlString(privateKey);
var encryptedBytes = rsa2.Encrypt(Encoding.UTF8.GetBytes(test), false);
var encryptedString = Convert.ToBase64String(encryptedBytes);
var rsa3 = new RSACryptoServiceProvider();
rsa3.FromXmlString(pubicKey);
encryptedBytes = Convert.FromBase64String(encryptedString);
var decryptedString = Encoding.UTF8.GetString(rsa3.Decrypt(encryptedBytes, false));
You can use a signature strategy, where the private key is used to generate a signature that verifies that your message is authentic.
// Create message and signature on your end
string message = "Here is the license message";
var converter = new ASCIIEncoding();
byte[] plainText = converter.GetBytes(secret);
var rsaWrite = new RSACryptoServiceProvider();
var privateParams = rsaWrite.ExportParameters(true);
// Generate the public key / these can be sent to the user.
var publicParams = rsaWrite.ExportParameters(false);
byte[] signature =
rsaWrite.SignData(plainText, new SHA1CryptoServiceProvider());
// Verify from the user's side. Note that only the public parameters
// are needed.
var rsaRead = new RSACryptoServiceProvider();
rsaRead.ImportParameters(publicParams);
if (rsaRead.VerifyData(plainText,
new SHA1CryptoServiceProvider(),
signature))
{
Console.WriteLine("Verified!");
}
else
{
Console.WriteLine("NOT verified!");
}
This example was largely copied from Microsoft's site:
RSACryptoServiceProvider.SignData Method (Byte[], Object)
And here is web page that explains the concept:
Using RSA for Signing Messages
I think what you are looking for is digital signature. It doesn't matter if the content is encrypted or not, since the user has the (public) key to decrypt it. All that matters is if the content's source is you.
Since you have a config file I reckon it is XML, so you are looking for XMLDSIG.
You can easily achieve this using the SignedXml class in .Net. Then all you need to do is verify the signature when loading the config file. This method allows you to eaisly use any X509 certificates you may have. You can even embed the public key in the signed file, so the user does not need to install your cert (public key).
Your idea is correct, but I wonder about unforeseen consequences.
You state they will be running software on their servers, so this means they are hosting a service for themselves. But you also mention this service has to connect out tot he internet to add a user by validating with your server. What happens when the internet goes down or they want to have a secure system and a firewall blocks internet access to the servers? Will they completely lose their ability to function?
Just giving you a question to ask yourself :p
You don't use a public key to 'decrypt' the file. you can only decrypt the file with the private key.
In your case you could store the number of users as well a signature of the data which you create with your private key.
On the clients sever, you use your public key to verify the signature matches the data in the file (number of users)
However, it is possible that an advanced user could swap out your public key with their own and sign the file themselves.
As stated in your comment in the question, your approach is using a key from a client and a key from yourself. This will not work, as the prime numbers used in RSA are meant for use with only the corresponding private/public key.
What you need to do use your two keys and nothing from the client or the client's two keys and nothing of yours. For example,
You could sign it using your two keys by encrypting with your private key and allowing the client to decrypt using your public key.
You could encrypt it using your client's public key and have them decrypt is using their (the client's) private key.
Hope this helps!

Digitally sign in PHP using private key, verify in C#

I am working on a feature that needs me to digitally sign a short string in PHP, and verify the string's signature in C#.
I would really like to use openssl_sign in PHP, because of its simplicity, but all the information I can find on Google indicates that this will not work.
There are some external libraries that claim to do this well, however as this is a hobby project I would rather not purchase such a library.
So what are the alternatives here? Full interoperability between C# and PHP is required. Libraries besides OpenSSL can be used.
I've done something very similar using Bouncy Castle Crypto APIs. It appears PHP openssl_sign uses SHA1 by default. If you are using anything other than the default you'll need to change the algorithm parameter for GetSigner.
string base64pubkey = "<!-- BASE64 representation of your pubkey from open ssl -->";
RsaKeyParameters pubKey = PublicKeyFactory.CreateKey(Convert.FromBase64String(base64pubkey)) as RsaKeyParameters;
byte[] signature = Convert.FromBase64String("<!-- BASE64 representation of your sig -->");
byte[] message = Encoding.ASCII.GetBytes("Something that has been signed");
ISigner sig = SignerUtilities.GetSigner("SHA1WithRSAEncryption");
sig.Init(false, pubKey);
sig.BlockUpdate(message, 0, message.Length);
if (sig.VerifySignature(signature))
{
Console.WriteLine("all good!");
}
You may use to check the digital signature smth like this:
string publicKey = "some key";
// Verifying Step 1: Create the digital signature algorithm object
DSACryptoServiceProvider verifier = new DSACryptoServiceProvider();
// Verifying Step 2: Import the signature and public key.
verifier.FromXmlString(publicKey);
// Verifying Step 3: Store the data to be verified in a byte array
FileStream file = new FileStream(args[0], FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(file2);
byte[] data = reader.ReadBytes((int)file2.Length);
// Verifying Step 4: Call the VerifyData method
if (verifier.VerifyData(data, signature))
Console.WriteLine("Signature verified");
else
Console.WriteLine("Signature NOT verified");
reader.Close();
file.Close();
Is there a reason you need something as complex as SSL signing? Can't you just use a simple one-way hash like MD5/SHA-1 on the string? If all you're looking for is verification that the string wasn't tampered with, that should be sufficient.
So looking at this - this guy appears to have asymmetric signing and encrypting working between PHP and C#. Signing should not be a problem, SHA* and MD* are standard, and so it's very very unlikely that is going to not be compatible (although you should be looking at SHA256 as MD* and SHA1 are deprecated due to vulnerabilities)
We're missing some context as to why you need to sign it. You may not need to.
The important question is: what guarantees do you need from your data?
If all you need to do is verify the integrity of the data, a hash will do the job. If you need to verify where it's coming from, you need to sign it. If you need both, hash it, concatenate the payload with the hash, and sign the whole thing.
Regarding cross-platform libraries... you really should need to worry about it. A SHA1 is a SHA1 is a SHA1, no matter which library generated it. Same thing with generating and verifying digital signatures. Use what's easiest in PHP and use what's easiest in C#. If they're both set up correctly you shouldn't need to worry about it.

Categories

Resources