SecureStore 1.2.2
dotnet add package SecureStore --version 1.2.2
NuGet\Install-Package SecureStore -Version 1.2.2
<PackageReference Include="SecureStore" Version="1.2.2" />
paket add SecureStore --version 1.2.2
#r "nuget: SecureStore, 1.2.2"
// Install SecureStore as a Cake Addin #addin nuget:?package=SecureStore&version=1.2.2 // Install SecureStore as a Cake Tool #tool nuget:?package=SecureStore&version=1.2.2
SecureStore: the .NET Secrets Manager
SecureStore is an open, symmetrically-encrypted secrets file format that proudly embraces the KISS mentality, attempts to balance security with ease-of-use, and eschews overly-complicated solutions where possible.
SecureStore Architecture
While "how to use" might be a more typical first section of the README for libraries, as a security-oriented library, we feel that "how it works" better deserves the initial focus.
Unlike other secrets managers for .NET, SecureStore attempts to walk the fine line between security and pragmatism. All secrets remain encrypted at rest, and there are no needless dependencies such as Docker or a separate secrets manager server.
At its heart, SecureStore secrets are stored in a JSON file on-disk (to make sure getting your data out is as easy as can be), encrypted via AES-128-CBC with a HMAC-SHA1 verification step for ciphertext authentication. The secrets file is formatted and maintained in a way that makes it version-control-friendly - you can both safely include the secrets file in your project repository (read more about why you not only can but also should do so) and we try to make it diff-friendly by never needlessly re-encrypting stored secrets and stably sorting its contents in a deterministic fashion so your version control system does not go haywire just because you added a new secret or changed an existing one.
For encryption/decryption, SecureStore can either use a key safely derived from a password of your choosing (currently via 256,000 rounds of PBKDF2 SHA1), generate a randomly-created key file, or use a pre-existing key securely created via other means of your choosing. Attempts are made to secure unencrypted data in memory by preventing GC reallocations and only keeping decrypted content as long as needed in memory, overwriting it when its no longer needed. However, due to the design of the .NET language and the restrictions on unsafe memory access, no guarantees are made about the permanence of decrypted content in the memory and we do not consider protecting against a compromised machine to be a design goal. If you're not sure what this means, don't worry, you should be safe. This is more of an academic detail for most people, and won't impact in any way the security of your secrets store, which you can feel free to publish on the front page of The New York Times and have nothing to worry about.
Everything is self-contained in the SecureStore .NET library, available as a NuGet package. Creating a secrets file and adding secrets to it may be done via the same SecureStore API used for secrets retrieval or you can install the separate, standalone CLI companion app to more easily manage your secrets interactively.
You can read more about the architectural decisions that drive SecureStore in the original release notes and the updated article covering the cross-platform 1.0 release.
Obtaining SecureStore
The SecureStore.NET library is currently available for all versions of the .NET Framework and .NET Core and can be obtained by either locally cloning this git repository and building it or via NuGet:
Install-Package SecureStore
The companion CLI app for creating and managing secrets files can be installed the same way as all the other dotnet
tools:
dotnet tool install --global SecureStore.Client
after which it is available for use in your shell as SecureStore
(e.g. SecureStore create secrets.json
). More information on using the command line utility can be found in its documention.
Using SecureStore
As previously mentioned, SecureStore puts a premium on ease-of-use, and you can be up and going in minutes once you've added SecureStore to your .NET or ASP.NET project.
Everything starts and ends with a SecretsManager
object, available in the NeoSmart.SecureStore
namespace. The static methods SecretsManager.CreateStore
and SecretsManager.LoadStore
can be used to either create a new store or load an existing store; for your security and in an embrace of self-documenting APIs, the constructor for SecretsManager
has been disabled - a SecretsManager
instance can only be created via one of these two means.
Important note: SecretsManager
implements IDisposable
and must only be instantiated in a using
block! There is important cleanup of crypto resources in the Dispose()
method!
Creating Secrets
Your secrets can be created via the .NET SecretsManager
API or via the standalone SecureStore
CLI utility.
Creating secrets with the .NET API
As mentioned above, the encryption/decryption key used by SecureStore can be created by SecretsManager
, securely derived from a password of your choice, or loaded from an existing key file provided by yourself. All options are shown below:
// A using block MUST be used for security reasons!
using (var sman = SecretsManager.CreateStore())
{
// Create a new key securely with a CSPRNG:
sman.GenerateKey();
// or use an existing key file:
sman.LoadKeyFromFile("path/to/file");
// or securely derive key from passsword:
sman.LoadKeyFromPassword("p4ssw0rd!");
sman.Set("foo", "bar");
sman.Set("the answer", new byte[] { 42 });
// Optionally export the keyfile (even if you created the store with a password)
sman.ExportKey("secrets.key");
// Then save the store if you've made any changes to it
sman.SaveStore("secrets.bin");
}
The above code snippet demonstrates a few important points when it comes to using SecretsManager
:
- A
using (...)
block must be used when creating or loading aSecretsManager
instance. - Any one (and only one) of the three methods (
GenerateKey()
,LoadKeyFromFile(path)
, orLoadKeyFromPassword(password)
may be used to load a private key into memory. An attempt to load multiple keys will cause aKeyAlreadyLoadedException
to be thrown. - The
SecretsManager.Set<T>(key, value)
is a generic function natively supporting the serialization of strings and binary lists/arrays. An option to override the default serializer with your own to provide support for other secret types is available. - The
SecretsManager.ExportKey(path)
API can be used to export the key that was loaded into memory. Use this interface with obvious caution. This is obviously required when using theGenerateKey()
API to initialize the private key used by theSecretsManager
interface, but what may not be obvious is that it can be used to create a dual-decryptable store by creating a store with a password but also exporting the associated key. This lets you manage the store using the command line utility interactively with the convenience of a password but transmit the equivalent keyfile to your server for passwordless decryption in production. - Unless
SecretManager.SaveStore(path)
is called, nothing is ever written to disk, including if an existing store was loaded and its contents modified.
Additionally, secrets may be appended to a previously-created store by calling SecretsManager.LoadStore(path)
instead of SecretsManager.CreateStore()
.
Creating secrets with SecureStore
CLI
The SecureStore command line interface may also be used to create a secure store. A encryption key may be supplied as a password in a command line argument (if -p
or --password
is followed by a value), entered securely via stdin
(if -p
is not provided a value), or from a key file (via a value provided to the -f
or --keyfile
parameter). To create a new key file, simply specify --keyfile
value pointing to a path that does not exist and a new key will be generated, used, and saved to the named path.
SecureStore create secrets.json
SecureStore --store secrets.json set foo bar
If no path to a store is provided at the command line, the default store secrets.json
will be used.
See the full client documentation for further details.
Retrieving Secrets
Retrieving secrets may also be done via both the .NET API and the SecureStore
CLI. Options exist to iterate over all secrets in a secrets file and to query for the existence of a key in an exception-safe manner.
Retrieving secrets via the .NET API
The API to retrieve secrets does not differ greatly from that used to create them. Usage is straight-forward:
using (var sman = SecretsManager.LoadStore("secrets.bin"))
{
sman.LoadKeyFromFile("secrets.key");
var secret = sman.Get("foo");
if (!sman.TryGet("the answer", out byte[] answer))
{
Console.WriteLine("Secret \"the answer\" not found in store!");
}
}
All secrets in a secrets file may be obtained by iterating over the SecretsManager.Keys
value, which implements IEnumerable
:
using (var sman = SecretsManager.LoadStore("secrets.bin"))
{
sman.LoadKeyFromPassword("p4ssw0rd!");
foreach(var k in sman.Keys)
{
Console.WriteLine($"{k}: {sman.Get(k)}");
}
}
Needless to say, this should be used with extreme caution.
Retrieving secrets via the SecureStore
CLI
The SecureStore
utility also provides similar functionality and can be used to both retrieve a single value or to export a decrypted copy of the store.
SecureStore get -s secrets.bin foo
The above will prompt for a password and then echo bar
to the command line. No additional text is printed, making it suitable for inclusion in scripts or other workflows that rely on piping or parsing stdout
content. The default mode of operation is --password
/-p
, which may be omitted as above.
A copy of all encrypted data in a store may be obtained via ssclient get --all
in either json (--format json
, default) or plain text (--format text
) formats:
SecureStore get --all -s secrets.bin --keyfile secrets.key
and
SecureStore get --all --format text -s secrets.bin -p
License and Copyright
SecureStore for .NET is written and developed by Mahmoud Al-Qudsi of NeoSmart Technologies. SecureStore is released to the general public under the terms of the open source MIT license, and all rights not assigned are reserved to NeoSmart Technologies. The names SecureStore
and SecureStore.NET
are copyright NeoSmart Technologies 2015 - 2022.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.3)
- System.Memory (>= 4.5.5)
-
.NETStandard 2.1
- Newtonsoft.Json (>= 13.0.3)
-
net6.0
- Newtonsoft.Json (>= 13.0.3)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on SecureStore:
Package | Downloads |
---|---|
SecureStore.Contrib.Configuration
A SecureStore configuration provider to use with Microsoft.Extensions.Configuration. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.2.2 | 27,188 | 2/27/2024 |
1.2.1 | 1,818 | 1/22/2024 |
1.2.0 | 3,697 | 8/29/2023 |
1.2.0-preview2 | 630 | 3/30/2023 |
1.2.0-preview1 | 1,068 | 12/13/2022 |
1.1.0 | 8,006 | 6/30/2022 |
1.0.3 | 70,063 | 1/27/2020 |
1.0.2 | 1,048 | 1/27/2020 |
1.0.1.1 | 1,034 | 1/22/2020 |
1.0.1 | 1,014 | 1/22/2020 |
1.0.0 | 9,417 | 1/22/2020 |
0.3.3 | 3,647 | 12/15/2017 |
0.3.2 | 1,567 | 12/15/2017 |
0.3.1 | 1,607 | 12/15/2017 |
0.3.0 | 1,530 | 12/13/2017 |
0.2.0 | 1,488 | 8/27/2017 |
Cross-platform release