If authorized users can modify the configuration file, an asymmetric key will be used to sign the file for a higher-level approach. Only authorized persons who have access to the private key can generate the hash, but the application can verify the legitimacy of the hash (and therefore the file itself) with the public key only. The next quick run.
This implementation requires the generation of three files:
- The configuration file you want to sign.
- A file available for an application containing a hash of a configuration file.
- Personal configuration file containing the parameters of the private key.
Files (1) and (2) are accessed by the application. File (3) is confidential, limited to authorized users.
The main mechanism is as follows:
- Create an RSA key pair and save the public and private key information. The secret key information stored in the file (3) and the public key information are included in the application. This step is performed only once.
- When changing the configuration file, file (3) is used to sign the SHA1 hash of the file. The hash is saved in file (2).
- Whenever a program loads a configuration file, it generates a hash of the configuration file and uses the public key to verify the signature. If the signatures match, it continues; if they do not match, this throws an exception.
1. Creating an RSA Key Pair
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 creates the 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>
Rows of private keys must be saved (via any mechanism) to a file (3) (private key). An application confirming the integrity of the file will require a public key string (this is discussed further in step 3).
2. Signing the configuration file
To sign the configuration file, you will need the Xml string of the private key 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) }
The GetConfigurationFileSignature call returns a base64 encoded string. Save this line in the file (2) to which the application will refer.
3. Check the integrity of the configuration file
When the configuration file is downloaded, the application should verify the signature of the configuration file for digital signature. The public key must be stored in the application, not in the configuration file. Otherwise, the attacker can simply overwrite the public key in the configuration file, the 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 to 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 a known signature. If this method returns false, it indicates that the file has been modified; if it returns true, this indicates that the file is legal.
Final notes
- Cryptography only provides the creation of a generated digital signature using a private key. An attacker can still override the application validation logic to read malicious configuration data.
- Any change to the configuration file (including adding comments, spaces, etc.) will cause the hash to become invalid. A more specialized implementation of
GetFileSha1Hash can search for specific key / value pairs in XML and sign only that data, allowing other changes 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 in the file will GetFileSha1Hash previous hash. The specialized implementation of GetFileSha1Hash can ignore the generated hash value when generating the hash of the configuration file, thereby preventing the need for separate configuration files.