I'm building a multi-tenant (cloud) environment that needs to push out (or make available for download) configuration files for agents (background services) that receive this information. One of the pieces of information in the config is a username and password.
Assuming that the agent can be identified securely to the cloud, what encryption and security system would you use to encrypt, share, push out this sensitive information?
Is a Public Private key pair sufficient? I am thinking the secrets will be encrypted to the public key of each agent, and the unencrypted value will be discarded.
What are your thoughts on this implementation? I'll mostly be using C# in this application, Windows Azure, ASP.NET MVC, and Silverlight.
Sample Agent-side code (RSACryptoProvider)
This will generate the Public Private Key Pair in C#, and not save the key to disk
public static void AssignNewKey(){
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "KeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
// CspProviderFlags.UseNonExportableKey -- Prevent less-knowledgeable attacks against PK
// CspProviderFlags.UseUserProtectedKey -- Interactively prompt for password
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
rsa.PersistKeyInCsp = false;
string publicPrivateKeyXML = rsa.ToXmlString(true);
string publicOnlyKeyXML = rsa.ToXmlString(false);
// do stuff with keys...
}
Sample Agent-side code Option 2 (Bouncy Castle)
public void GenerateKey(string username, string password, string keyStoreUrl)
{
IAsymmetricCipherKeyPairGenerator kpg = new RsaKeyPairGenerator();
kpg.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(0x13), new SecureRandom(), 1024, 8));
AsymmetricCipherKeyPair kp = kpg.GenerateKeyPair();
FileStream out1 = new FileInfo(string.Format("{0}secret.asc", keyStoreUrl)).OpenWrite();
FileStream out2 = new FileInfo(string.Format("{0}pub.asc", keyStoreUrl)).OpenWrite();
ExportKeyPair(out1, out2, kp.Public, kp.Private, username, password.ToCharArray(), true);
out1.Close();
out2.Close();
}
private static void ExportKeyPair(
Stream secretOut,
Stream publicOut,
AsymmetricKeyParameter publicKey,
AsymmetricKeyParameter privateKey,
string identity,
char[] passPhrase,
bool armor)
{
if (armor)
{
secretOut = new ArmoredOutputStream(secretOut);
}
PgpSecretKey secretKey = new PgpSecretKey(
PgpSignature.DefaultCertification,
PublicKeyAlgorithmTag.RsaGeneral,
publicKey,
privateKey,
DateTime.Now,
identity,
SymmetricKeyAlgorithmTag.Cast5,
passPhrase,
null,
null,
new SecureRandom()
// ,"BC"
);
secretKey.Encode(secretOut);
secretOut.Close();
if (armor)
{
publicOut = new ArmoredOutputStream(publicOut);
}
PgpPublicKey key = secretKey.PublicKey;
key.Encode(publicOut);
publicOut.Close();
}
"Assuming ... can be identified securely to the cloud"
- why would you assume that? How can you ensure it? And, assuming you can (e.g. via private/public keys) - why do you then need to (potentially) expose passwords in client-side config files? You can can already authenticate them "somehow" - so why bother with an additional datum that needs to be kept secret? Of course, that's assuming the password is intended to identify individual agents - otherwise, why ever bother? ... Then again, maybe I'm missing something..."assuming... can be identified securely to the cloud"
may have been too brief on my part. I wanted to focus on one piece of this equation, but feel free to ask about the rest if you want more detail.