I want to be able to detect when a critical configuration file has been changed.
I've used Configuration Section Designer to create a simple configuration file but now I cannot find an easy way to encrypt the result or add a value to it to check that it has been changed.
I want to be able to either prevent unauthorized users from changing the config file or at least know if the file has been changed.
If authorized users can change the configuration file, a high-level approach would be use an asymmetric key to sign the file. Only authorized individuals with access to the private key would be able to generate the hash, but the application could verify the legitimacy of the hash (and thus the file itself) with only the public key. One quick implementation follows.
This implementation requires generation of three files:
The configuration file you want to sign.
A file accessible to the application that contains the configuration file hash.
A private configuration file that contains the private key parameters.
Files (1) and (2) are accessed by the application. File (3) is confidential, restricted to authorized users.
The basic mechanism is this:
Generate an RSA key pair, and save the public and private key information. Private key information saved into File (3), and public key information is incorporated into the application. This step is performed only once.
Whenever a configuration file is changed, file (3) is used to sign the file's SHA1 hash. The hash is saved into file (2).
Whenever the program loads the configuration file, it generates the configuration file hash, and uses the public key to verify the signature. If the signatures match, it continues; if they do not match, it throws an exception.
1. Generating the RSA key pair
The RSA key data can be generated as XML:
(use System.Security.Cryptography)
var csp = new RSACryptoServiceProvider();
string publicXml = csp.ToXmlString(false);
string privateXml = csp.ToXmlString(true);
This produces XML data in the following format.
publicXML:
<RSAKeyValue>
<Modulus>oQKZR9hHrqm1tauCFYpbFlwyRNIHeyc2HCX+5htF/oc1x8Nk8i+itTzwRlgQG1cICO6lX
A+J9/OO2x2b9JILtk2tQow10xJdIsuiBeRwe7wJRdS8+l21F/JPY0eu/xiKQy
ukzEWLjIxGX7UXb9e4ltIxyRUUhk5G/ia1trcxfBc=
</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
privateXml:
<RSAKeyValue><Modulus>oQKZR9hHrqm1tauCFYpbFlwyRNIHeyc2HCX+5htF/oc1x8Nk8
i+itTzwRlgQG1cICO6lXA+J9/OO2x2b9JILtk2tQow10xJdIsuiBeRwe7wJRdS8+l21F/JPY0eu/xiKQ
yukzEWLjIxGX7UXb9e4ltIxyRUUhk5G/ia1trcxfBc=</Modulus><Exponent>AQAB</Exponent>
<P>zpFEWa7ave3wHL7pw7pSG0KXDPRwhCzU1Z5/fLoqSrPQzbkRqU+cwDVO/6IId3HdeXE09kVIu9/HBId
vupnY9w==</P><Q>x4pmqkmB7i8g9d3G6RSeZWYde8VOS5/OHUKgM6VrlQhgyrATpxGWAzJAe5eNO2BU
axNO8fZPe+lUSCJgY6TN4Q==</Q><DP>jaNL05ayhDLHRl6dmUiDjg+N1SMyl17KHSON1O8tmoVLchQp
CQf+ukiTP3NSDNy1eNTn9MkzAyeAphlbwf5Fpw==</DP><DQ>HhmUjw9zmBhn4m7H+JTxp94ViHwk6Wd
70hIg1GmZpuuSnkCdVlBizqyf6YTc+x323ggVmo5LQyfZXOBCpgVQQQ==</DQ><InverseQ>iO0CKRGB
2ULS6is/SwonqJw5fBsI9HTzx8rmKGA189dwlLGJSJuQo8uWmrLYhuo22BAqd0lMqxlKCHv6leeGPw==
</InverseQ><D>RSLliJkRJqnO0cRkZjVzqWVLXIvHFJWgwXN7QXlik8mhSTbYqLnVpvcUwU/dErBLTf
KTZLVza9nUdLgBGIKBrkbIqIWixq1fQ3zsEkyB/FQxwhIerTrhHyPzR+i3+5mduqQ7EBTj64u6STUf0y
TXHW2FYlfAinNz+K3iQFFarEE=</D></RSAKeyValue>
The private key strings should be saved (through any mechanism) to File (3) (private key). The application that is confirming the file's integrity will need the public key string (discussed later in step 3).
2. Signing the configuration file
To sign the configuration file, you will need the Private key Xml string generated in step (1).
private byte[] GetFileSha1Hash(string file)
{
using (var fs = new FileStream(
file, FileMode.Open))
{
return new SHA1CryptoServiceProvider().ComputeHash(fs);
}
}
static string GetConfigurationFileSignature(string configfile, string privateXml)
{
var p = new RSACryptoServiceProvider();
p.FromXmlString(privateXml);
byte[] signature = p.SignHash(GetFileSha1Hash(configfile),
CryptoConfig.MapNameToOID("SHA1"));
return Convert.ToBase64String(signature)
}
Calling GetConfigurationFileSignature will return a base64-encoded string. Save this string into File (2), where it will be referenced by the application.
3. Check the configuration file integrity
When the configuration file is loaded, the application should check the configuration file signature against the digital signature. The public key should be stored within the application rather than in a configuration file -- otherwise, the attacker can simply overwrite the public key in the configuration file with one for which he/she knows the corresponding private key pair.
static bool VerifyConfigurationFileSignature(string fileName, string publicXml, string signature)
{
var p = new RSACryptoServiceProvider();
p.FromXmlString(publicXml);
return p.VerifyHash(
GetFileSha1Hash(fileName),
CryptoConfig.MapNameToOID("SHA1"),
Convert.FromBase64String(signature));
}
(Note: you can use AppDomain.CurrentDomain.SetupInformation.ConfigurationFile to get the path of the current configuration file.)
When the application loads, it can make a call to VerifyConfigurationFileSignature with the path to the configuration file, public key XML data, and known signature. If this method returns false, it indicates the file has been tampered with; if it returns true, it is evidence that the file is legitimate.
Final Notes
The cryptography only ensures that the generated digital signature was generated with the private key. An attacker may still be able to override the application's verification logic to read malicious configuration data.
Any change to the configuration file (including adding comments, whitespace, etc.) will result in the hash becoming invalid. A more specialized implementation of GetFileSha1Hash might search for specific key/value pairs in the XML and sign only that data, allowing other modifications to the configuration file.
Including the hash itself in the XML file is not possible with the above implementation of GetFileSha1Hash, since updating the hash itself in the file would render the prior hash invalid. A specialized implementation of GetFileSha1Hash can ignore the generated hash value in generating the configuration file hash, thus preventing the need for separate configuration files.
A common approach to determining whether something has changes is to take a 'hash', for example, you could take an MD5 hash of the configuration, or part of it, then check against this has each time it is loaded in order to determine if it has been changed.
Related
I have iText7 functions which I am using,
right now, I am trying to encrypt my PDF file using a certificate in .pfx format with password.
The thing is, the function cannot read .pfx because it does not provide the password as shown below
using System.IO;
using iText.Kernel.Pdf;
using Org.BouncyCastle.X509;
namespace iText.Samples.Sandbox.Security
{
public class EncryptWithCertificate
{
public static readonly String DEST = "results/sandbox/security/encrypt_with_certificate.pdf";
public static readonly String SRC = "../../../resources/pdfs/hello.pdf";
public static readonly String PUBLIC = "../../../resources/encryption/test.cer";
public static void Main(String[] args)
{
FileInfo file = new FileInfo(DEST);
file.Directory.Create();
new EncryptWithCertificate().ManipulatePdf(DEST);
}
public X509Certificate GetPublicCertificate(String path)
{
using (FileStream stream = File.Open(path, FileMode.Open))
{
X509CertificateParser parser = new X509CertificateParser();
X509Certificate readCertificate = parser.ReadCertificate(stream);
return readCertificate;
}
}
protected void ManipulatePdf(String dest)
{
// The file created by this example can not be opened, unless
// you import the private key stored in test.p12 in your certificate store.
// The password for the p12 file is kspass.
X509Certificate cert = GetPublicCertificate(PUBLIC);
PdfDocument document = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest,
new WriterProperties().SetPublicKeyEncryption(
new[] {cert},
new[] {EncryptionConstants.ALLOW_PRINTING},
EncryptionConstants.ENCRYPTION_AES_256)));
document.Close();
}
}
}
If i try to load a normal .cer file, it goes through normally for GetPublicCertificate. No issue there. But I am trying to encrypt it with .pfx file as adobe acrobat can only register Digital ID using .p12/.pkf format and the function does throws error.
The error
Org.BouncyCastle.Security.Certificates.CertificateException
HResult=0x80131500
Message=Failed to read certificate
Inner Exception 1:
ArgumentException: Unknown object in GetInstance: Org.BouncyCastle.Asn1.DerInteger
Parameter name: obj
I am hoping to encrypt the pdf using cert as the cert can be set to expire anytime according to what I set it to be and user can only view the PDF file based on the cert expiry.
Thanks in advance.
As per your original post, your intention is:
I am hoping to encrypt the pdf using cert as the cert can be set to expire anytime according to what I set it to be and user can only view the PDF file based on the cert expiry.
Basically, you want to grant a time-limited access to PDF to authorized user. The solution you try to build in code sample doesn't solve the problem. Certificate validity for data encryption is irrelevant, because certificate validity is not checked during decryption. In fact, even certificate is not necessary, it is sufficient to have just a private key to decrypt the data. In other words, certificate-based encryption is equal to password-based encryption. What certificate adds -- an easier way to locate decryption key (secret), nothing else.
In addition, once data is decrypted, a client can save data in an unencrypted form thus your restrictions are useless. Even if you try to put time constraints within JavaScript or whatever else locally (and JavaScript is executed only locally), it isn't a solution. As long as client can manipulate date/time on a device, client always can set desired date/time to violate your restrictions.
Your problem cannot be solved without inventing a 3rd party entity that will make decisions whether the requested operation is allowed, apply necessary restrictions and minimize chances that the data will leak in an unencrypted form (only minimize, not prevent). Such functionality is implemented in Digital Rights Management (DRM) or Rights Management Service (RMS) and you need to build your solution around these tools, not attempt to integrate them in your solution. There are plenty of vendors that offer DRM/RMS solutions you can look into and utilize their functionality to build the solution for your requirements.
I have a file testpublic.snk which holds a public key. I have created it with sn -p c:\test.snk c:\testpublic.snk.
Now, how can I convert testpublic.snk to a string like
<RSAKeyValue><Modulus>z140GwiEJvuGOVqMQUxnojILR8rn2SFOViigoloku59H5eqzqca3Hdyl/jc+Acdb5ktzhBOOyGFElE0/Btlvw9cXVVW8zcT0MBOCaq25D1rSVYLGGM6nXzBrl1XsrBEadZbCgkcF5rw8GaYcYakijaltP1/hvxhbMOARM9VCQ50=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
Thanks for your help.
Simply re-use the (MIT.X11 licensed) code from Mono.Security StrongName class, available in github, then call ToXmlString on it's RSA property. Something like:
Console.WriteLine (new StrongName (data).RSA.ToXmlString (false));
Where data is a byte[] containing the content of your snk file. Also you can use true if you want the private key in your XML (it will work if it was available fom your snk file).
I'm working on project that involves reading public key data used to sign Android APKs. I am able to successfully extract the signatures as public keys. When I look inside the binary pubkey files produced by this, I see some plain-text such as a name and a city.
How can I safely extract this name/city information embedded inside the public key using PHP (or even Java or C#)? And hopefully do it in such a way that I know exactly what these fields are (i.e. not blindly grabbing text, but knowing which string is a city and which is a name)
For clarification: I don't have the private key or a certificate file. I'm currently not interested in signing or encrypting anything, I would just like to extract the plaintext inside the pubkey without using kludgy approaches like regex.
Update: Here's a sample (base64-encoded) public key from one of my APKs
MIICBzCCAXCgAwIBAgIES6KlazANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQGEwJVUzELMAkGA1UECBMCUkkxFTATBgNVBAcTDE5hcnJhZ2Fuc2V0dDEVMBMGA1UEAxMMQ29saW4gTydEZWxsMB4XDTEwMDMxODIyMTI1OVoXDTQ1MDMwOTIyMTI1OVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlJJMRUwEwYDVQQHEwxOYXJyYWdhbnNldHQxFTATBgNVBAMTDENvbGluIE8nRGVsbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmPetcBW+ITURXY0LsI2ZfgM3R7K2kwicgpd0W+BYAXQBh76SXyN9MYvtfnUY3SNz37FW/lDQgAO3pbhEFqGwfADh2ctXlYmlE9DtcRQw0ojGVPIDlWBX+9IUxyL/89CPaN84R/1lvdosco4V0BqQYR300S9ZwmwFA2Vh9hSUZmsCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBezKu4G11Z68NTPIBro8xsnbkdYxObzW7BsSr6t9MS5x6EQVs75R/nnKrsMcQ+9ImdT940jhQgZT3ZrYla5VhdbelxnLhBVbJfBdipV3Hv2bG7MnXzFqHYwQqYp+UrP8zWm1YHQf5I/P9VBjlkgwFyNKr0TxP4t/qS08oGX2wvZg==
The string you put in is a base 64 encoded x509 certificate, not simply a public key.
You'll need to parse the Distinguished Name fields to get the desired info.
Here's a C# example:
using System;
using System.Security.Cryptography.X509Certificates;
namespace Sample
{
class Program
{
static void Main(string[] args)
{
string base64EncodedX509 =
"MIICBzCCAXCgAwIBAgIES6KlazANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQGEwJVUzELMAkGA1UECBMCUkkxFTATBgNVBAcTDE5hcnJhZ2Fuc2V0dDEVMBMGA1UEAxMMQ29saW4gTydEZWxsMB4XDTEwMDMxODIyMTI1OVoXDTQ1MDMwOTIyMTI1OVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlJJMRUwEwYDVQQHEwxOYXJyYWdhbnNldHQxFTATBgNVBAMTDENvbGluIE8nRGVsbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmPetcBW+ITURXY0LsI2ZfgM3R7K2kwicgpd0W+BYAXQBh76SXyN9MYvtfnUY3SNz37FW/lDQgAO3pbhEFqGwfADh2ctXlYmlE9DtcRQw0ojGVPIDlWBX+9IUxyL/89CPaN84R/1lvdosco4V0BqQYR300S9ZwmwFA2Vh9hSUZmsCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBezKu4G11Z68NTPIBro8xsnbkdYxObzW7BsSr6t9MS5x6EQVs75R/nnKrsMcQ+9ImdT940jhQgZT3ZrYla5VhdbelxnLhBVbJfBdipV3Hv2bG7MnXzFqHYwQqYp+UrP8zWm1YHQf5I/P9VBjlkgwFyNKr0TxP4t/qS08oGX2wvZg==";
var rawBytes = Convert.FromBase64String(base64EncodedX509);
X509Certificate cert = new X509Certificate(rawBytes);
// Parse the distinguished name to get your desired fields
Console.WriteLine(cert.Subject); // writes CN=Colin O'Dell, L=Narragansett, S=RI, C=US
Console.WriteLine(cert.Issuer); // writes CN=Colin O'Dell, L=Narragansett, S=RI, C=US
}
}
}
The "binary pubkey files produced by this" is an X.509 certificate.
Just about any platform has support for reading X.509 certificates, and creating a structure from them, from which you can reliably extract the "subject name," and often extended information that includes an email address or host name.
For example, if you have OpenSSL installed, use the following command:
openssl x509 -text -noout -inform der -in <yourfilehere>
You can extract specific fields with additional options. For example, adding -subject yields:
subject= /C=US/ST=RI/L=Narragansett/CN=Colin O'Dell
In php, I am not sure but maybe this function could be your friend. There is a whole section of ssl related functions that could come in handy if you are playing with certificates.
I want to create 2 really simple dlls:
1) that will sign an xml document
2) that will check that the xml document hasnt been modified.
I tried using the RSACryptoServiceProvider and a key container. But when I move to a different machine this does not work as the key is being stored in the machine.
I want to store the key in the dlls im creating (I know this is not reccomended) but I just cannot work out how to write some code to simply sign an xml document and then verify that it hasn't been changed.
So do I need to use symmetric key to do what I want is this possible?
Pete
You already mention the problems with storing the private key in the dll, so I won't repeat that.
Do this:
On your own machine run this code:
var key = new RSACryptoServiceProvider(2048);
string publicKey = key.ToXmlString(false);
string privateKey = key.ToXmlString(true);
Console.WriteLine(publicKey);
Console.WriteLine(privateKey);
this outputs two (long) lines. Copy those into your code:
Sign:
var privateKey = new RSACryptoServiceProvider();
privateKey.FromXmlString(/* insert the private-key XML string here */ );
privateKey.SignData(/*...*/);
Verify:
var publicKey = new RSACryptoServiceProvider();
publicKey.FromXmlString(/* insert the public-key XML string here */ );
publicKey.VerifyData(/*...*/);
If it is just about to verify that your xml document hasn't been modified a simple MD5 checksum (or any other good hashing algorithm) would be easier to implement and is what you need. It would be also verifyable on different machines.
it's late, I'm tired, and probably being quite dense....
I have written an application that I need to secure so it will only run on machines that I generate a key for.
What I am doing for now is getting the BIOS serial number and generating a hash from that, I then am encrypting it using a XML RSA private key. I then sign the XML to ensure that it is not tampered with.
I am trying to package the public key to decrypt and verify the signature with, but every time I try to execute the code as a different user than the one that generated the signature I get a failure on the signature.
Most of my code is modified from sample code I have found since I am not as familiar with RSA encryption as I would like to be. Below is the code I was using and the code I thought I needed to use to get this working right...
Any feedback would be greatly appreciated as I am quite lost at this point
the original code I was working with was this, this code works fine as long as the user launching the program is the same one that signed the document originally...
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "XML_DSIG_RSA_KEY";
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
// Create a new RSA signing key and save it in the container.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams)
{
PersistKeyInCsp = true,
};
This code is what I believe I should be doing but it's failing to verify the signature no matter what I do, regardless if it's the same user or a different one...
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
//Load the private key from xml file
XmlDocument xmlPrivateKey = new XmlDocument();
xmlPrivateKey.Load("KeyPriv.xml");
rsaKey.FromXmlString(xmlPrivateKey.InnerXml);
I believe this to have something to do with the key container name (Being a real dumbass here please excuse me) I am quite certain that this is the line that is both causing it to work in the first case and preventing it from working in the second case....
cspParams.KeyContainerName = "XML_DSIG_RSA_KEY";
Is there a way for me to sign/encrypt the XML with a private key when the application license is generated and then drop the public key in the app directory and use that to verify/decrypt the code? I can drop the encryption part if I can get the signature part working right. I was using it as a backup to obfuscate the origin of the license code I am keying from.
Does any of this make sense?
Am I a total dunce?
Thanks for any help anyone can give me in this..
I used this method to sign xml documents using a private key stored in an xml file that I then embedded into the application .dll as a resource. I think you may be struggling with permissions accessing the keystore, and this would also create hassles transferring the code to other servers etc.
Here is the code to get the private key as an embedded resource and sign the document:
(Sign is the name of the class this method is located in, Licensing.Private.Private.xml is a combination of the default namespace + folder + filename of the resource)
public static void SignDocument(XmlDocument xmldoc)
{
//Get the XML content from the embedded XML privatekey.
Stream s = null;
string xmlkey = string.Empty;
try
{
s = typeof(Sign).Assembly.GetManifestResourceStream("Licensing.Private.Private.xml");
// Read-in the XML content.
StreamReader reader = new StreamReader(s);
xmlkey = reader.ReadToEnd();
reader.Close();
}
catch (Exception e)
{
throw new Exception("Error: could not import key:",e);
}
// Create an RSA crypto service provider from the embedded
// XML document resource (the private key).
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
csp.FromXmlString(xmlkey);
//Creating the XML signing object.
SignedXml sxml = new SignedXml(xmldoc);
sxml.SigningKey = csp;
//Set the canonicalization method for the document.
sxml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigCanonicalizationUrl; // No comments.
//Create an empty reference (not enveloped) for the XPath transformation.
Reference r = new Reference("");
//Create the XPath transform and add it to the reference list.
r.AddTransform(new XmlDsigEnvelopedSignatureTransform(false));
//Add the reference to the SignedXml object.
sxml.AddReference(r);
//Compute the signature.
sxml.ComputeSignature();
// Get the signature XML and add it to the document element.
XmlElement sig = sxml.GetXml();
xmldoc.DocumentElement.AppendChild(sig);
}
Use the following code the generate the private.xml and public.xml keys. Keep the private.xml file secure, obviously.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
File.WriteAllText(#"C:\privateKey.xml", rsa.ToXmlString(true)); // Private Key
File.WriteAllText(#"C:\publicKey.xml", rsa.ToXmlString(false)); // Public Key
Guess, the problem is that different users don't have access to the key that is stored for the 1st user (Please note: I am not a cryptography expert).