Oftmals ist es erforderlich, in .NET Applikationen Zugangsdaten zu externen Systemen zu hinterlegen. Besonders wenn die Applikation selbständig, d.h. ohne Benutzer-Interaktion laufen soll, wird das Passwort häufig im Klartext entweder im Code oder in der app.config hinterlegt:
<setting name="Password" serializeAs="String"> <value>PlaintextPassword</value> </setting>
Ist es nicht möglich, die app.config durch Dateirechte so abzusichern, dass nur berechtige Benutzer die Datei lesen können, so sollte das Passwort idealerweise verschlüsselt in der app.config abgelegt werden. .NET bietet hierfür eine recht einfach zu verwendende Schnittstelle, das sogenannte Data Protection Application Programmer’s Interface, kurz DPAPI.
DPAPI ist Bestandteil des Betriebssystems und verschlüsselt/entschlüsselt wahlweise auf Benutzerebene oder auf Maschinenebene.
In meiner Applikation habe ich die Verschlüsselung auf Maschinenebene gewählt, da die Applikation mit unterschiedlichen Benutzeraccounts laufen soll und es so auch einfacher ist, ein Passwort direkt in der app.config zu konfigurieren. Mit der Verschlüsselung auf Maschinenebene können verschlüsselte Daten nur auf jenem Rechner entschlüsselt werden, auf dem sie verschlüsselt wurden.
Um das Passwort in der app.config konfigurieren zu können, wird das Passwort zunächst unverschlüsselt in die app.config eingetragen. Beim ersten Start der Applikation wird erkannt, dass das Passwort noch unverschlüsselt ist. Die Applikation verschlüsselt daraufhin das Passwort mittels DPAPI und schreibt das verschlüsselte Passwort zurück in die app.config. Ab diesem Zeitpunkt bekommt die Applikation nur mehr das verschlüsselte Passwort und kann es mit DPAPI wieder entschlüsseln. Es kann jederzeit ein neues Passwort konfiguriert werden, indem das neue Passwort zunächst im Klartext in die app.config eingetragen und die Applikation einmal gestartet wird.
Einen kleine Unschönheit gibt es allerdings: Mit DPAPI können in der app.config nur komplette Config Sections verschlüsselt werden und beim Entschlüsseln bekommt man die gesamte Config Section als XML-String. Um also an die verschlüsselten Parameter wieder heranzukommen, muss der XML-String geparst werden.
Und nun zum Code:
Für meiner Applikation habe ich in der app.config eine eigene „protectedSection“ definiert um nicht alle Konfigurationen verschlüsseln zu müssen. In dieser „protectedSection“ lege ich neben dem Passwort auch noch den Benutzernamen ab. Da DPAPI die gesamte Section verschlüsselt, wird also auch der Benutzername verschlüsselt.
// Protected Section lesen
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection(„applicationSettings/protectedSection“);
if (section != null)
{
if (!section.SectionInformation.IsProtected && !section.ElementInformation.IsLocked)
{
// Die Protected Section ist nicht verschlüsselt => Jetzt verschlüsseln und in die app.config zurückschreiben.
section.SectionInformation.ProtectSection(„DataProtectionConfigurationProvider“);
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
}
// Inhalt der unverschlüsselten Protected Section parsen.
var settingsXml = new XmlDocument();
settingsXml.LoadXml(section.SectionInformation.GetRawXml());
string user = settingsXml.SelectSingleNode(„//setting[@name=’Username‘]/value“).InnerText;
string pwd = settingsXml.SelectSingleNode(„//setting[@name=’Password‘]/value“).InnerText;
}
In app.config muss die protected Section deklariert werden:
<configSections>
<sectionGroup name=“applicationSettings“ type=“System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, […]“ >
<section name=“App.Properties.Settings“ type=“System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, […]“ />
<section name=“protectedSection“ type=“System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, […]“ />
</sectionGroup>
</configSections>
Und die eigentliche protected Section eintragen:
<applicationSettings>
<App.Properties.Settings>
[…]
</App.Properties.Settings>
<protectedSection>
<setting name=“Username“ serializeAs=“String“>
<value>DBUser</value>
</setting>
<setting name=“Password“ serializeAs=“String“>
<value>pa$$word1</value>
</setting>
</protectedSection>
</applicationSettings>
Nachdem die Applikation einmal gestartet wurde, sieht die protected Section in der app.config so aus:
<protectedSection configProtectionProvider=“DataProtectionConfigurationProvider“>
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAOBGysxLMxUWXFTYx0MJMMgQAAA[…]</CipherValue>
</CipherData>
</EncryptedData>
</protectedSection>
Ist doch cool, oder?