Currently I have a hard-coded secret key I use for my JWT Token Generation. What is the best way to generate this randomly when generating the token? Also, what I don't understand is if the secret is randomly generated, how can it be that the secret would be randomly generated again for authentication purposes. Am I missing something here or am I way off on how this works? It appears that the secret key is not even random. Is it something I would store in web.config for example
Just expanding on #nodd13's post to I have used the following (in LinqPad) to randomly generate a key:
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = Convert.ToBase64String(key);
// make safe for url
var urlEncoded = base64Secret.TrimEnd('=').Replace('+', '-').Replace('/', '_');
urlEncoded.Dump();
This is indeed random and as I understand it you only need to do this once and you can then store this in your web.config to be referenced later.
I used the following code from this blog post
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = TextEncodings.Base64Url.Encode(key);
Related
I am making a Windows Service which at certain points in its execution requires logging into a server using a username and password so I have been faced with the security issue of where to store these login details.
Initially I was going to use the Windows Credential Manager, but due to the user access of Windows Services (I need to use the Local System account) it was not the best option. I have settled on storing an encryption key by using an RSACryptoServiceProvider, like so:
using rsa = new RSACryptoServiceProvider(new CspParameters
{
Flags = CspProviderFlags.UseMachineKeyStore,
KeyContainerName = "UniqueKeyId"
});
which I can later use to decrypt the usernames and passwords I store in their encrypted, base64 forms, like so:
"MyLoginInfo":
{
"Username" : "LettWH4VMbvEG/OUBXGLluyceEG8Gon1fOytQ0IoKys18KSCfz6lc8fVO6XxBJtnSzjR23OAoE9TxG9lIqSRdpDwGGaTLGa3bpTGxzKlq+3OoLRo4Hf+9VEn/GO/UEZnzAmalPLErQO87krPmJuWCDqTthtPmi2Kh9jbcavz7Ss=",
"Password" : "Pnq3KoPip2WHpnDQivc8b0VOMzFn0W/OtSIVSELSE8SNqJSiRHa/6Yt47ndpyZRe6hTSvz3RZeLxaeQ+X1QIm1VRxESzbgz3ZFFzxy6F2ZJAikygWBhzNnu3jywG6u1C7amN7IO9/dHu2T6Jw8n6U1MTYgN2bV4lmGNHJ2bwAnA="
}
So my question is, what is to stop this from being called by some external process:
using rsa = new RSACryptoServiceProvider(new CspParameters
{
Flags = CspProviderFlags.UseMachineKeyStore,
KeyContainerName = "UniqueKeyId"
});
rsa.PersistKeyInCsp = false;
rsa.Clear();
And my key being eradicated, making decryption impossible at a later time? I'm not sure it would likely ever happen but the possibility is bothersome. Should I make my key container name extremely unique, like a GUID or hash of some kind? No one would be able to "browse" through all the available keys, would they?
I have to implement key rotations in my application. I have some idea how to do that but I am not sure if everything is OK with that solution.
Ok, lets start. I have couple places in my application where I use the KeyVaultClient(Azure KeyVault client) for decrypting purpose. It's work preety well. In my application are some places where KeyVaultClient is used for encrypting. For now(still development phase) I am using hardcoded params(vaultBaseUrl, keyName, keyVersion). But I want to go further and move this params to app.config file.
And here the question begun what to do with keyVersion variable(rest of them I think I can easy store in app.config file, isn't it?) I have couple ideas:
For encrypting:
I could store current keyVersion in app.config, and use this value each time I will encrypt data.
I could read from KeyVaultClients all Keys(GetKeysAsync), next filter them by active flag and order by expiration date. Finally use the newest one.
For decrypting:
I could store keyVersion used for encryption in encrypted data(the encryption result I am converting to Base64String). I mean I could add to string result the 32 characters(keyVersion) prefix.
No more ideas, maybe using the keyVersion from app.config, but it creates problem with keys rotations.
Maybe there is some tool/library that handle this all work for me? :p
For now, new keys are inserted manually, by administrator. In next phases I am going to implement scheduled task for that.
Maybe there is some tool/library that handle this all work for me?
As you mentioned that you need to encrypt key version is consistent with decrypting key version. It is better that if you could share your scenario. Take Encrypt blob for example. If the blob is encrypted,it will have a Metadata["encryptiondata"] with keyId in it. In your case, maybe you also could add a property with keyId for the object. When you try to decrypt then you could get the keyId from the object.
For now, new keys are inserted manually, by administrator. In next phases I am going to implement scheduled task for that.
If you want to create keys, you could do that with following this code sample in WebJob or Azure function.
static string _clientId= "xxxxxxxxxxxxx";
static string _clientSecret = "xxxxxxxxxxxxxxxx";
static string _tenantId = "xxxxxxxxxxxxxxxx";
public static async Task<string> GetAccessToken(string azureTenantId, string azureAppId, string azureSecretKey)
{
var context = new AuthenticationContext("https://login.windows.net/" + _tenantId);
ClientCredential clientCredential = new ClientCredential(_clientId, _clientSecret);
var tokenResponse = await context.AcquireTokenAsync("https://vault.azure.net", clientCredential);
var accessToken = tokenResponse.AccessToken;
return accessToken;
}
var kv = new KeyVaultClient(GetAccessToken);
var result = kv.CreateKeyAsync(vault,keyName,keyType).Result;
I need to sign and encrypt a SOAP request with a certificate to access a WS method, but i'm getting the same response all the time:
"A security error was encountered when verifying the message".
I guess there's something wrong with my code rather than any other issue.
Here it is:
cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(sCertificatePath, sCertificatePassword);
userToken = new Microsoft.Web.Services3.Security.Tokens.UsernameToken(sUser, sPass, Microsoft.Web.Services3.Security.Tokens.PasswordOption.SendHashed);
secureToken = new Microsoft.Web.Services3.Security.Tokens.X509SecurityToken(cert);
encDataToken = new Microsoft.Web.Services3.Security.EncryptedData(secureToken);
mSignUsernameToken = new Microsoft.Web.Services3.Security.MessageSignature(userToken);
mSignSecurityToken = new Microsoft.Web.Services3.Security.MessageSignature(secureToken);
wsVehicleInfo = new wsBusiness.VehicleInfoWSImplService();
vehData = new wsBusiness.getVehicleInfoRequest();
vehData.vehicleRegistration = "XXXXYYY";
vehData.language = "es";
requestContext = wsVehicleInfo.RequestSoapContext;
requestContext.Security.Elements.Add(encDataToken);
requestContext.Security.Tokens.Add(secureToken);
requestContext.Security.Elements.Add(mSignSecurityToken);
requestContext.Security.Timestamp.TtlInSeconds = 300;
requestContext.Security.Tokens.Add(userToken);
Is it correct? Actually I got some questions:
I'm signing and encrypting with the same certificate issued by a CA. Don't I need to encrypt with the server's one? How can I get it?
Does order of XML elements generated matter in the request? Which should be the code order?
The algorithm used in the documentation to encrypt the soap body is "aes128-gcm" but I wasn't able to find it and instead using "aes128-cbc". May it cause any trouble?
Is it recommended the usage of WSE in this case? I read this:
"Instead of asymmetrically encrypting the message, WSE use an asymmetric algorithm with a public copy of the recipient's X.509 certificate to encrypt the symmetric key that was actually used to encrypt the message data.". No way to just encrypt the SOAP body from the request with the server's certificate with WSE instead of encrypting the symmetric key?
I've been struggeling with this for days now and RFC 2315 is a bit hard to understand.
I'm trying to implement my own version of EnvelopedCms.Decrypt(), so that I can use the certificate operations of Azure Key Vault to UnwrapKey and/or Decrypt a PKCS#7 message (CMS Object) in a correct way. I use EnevelopedCms in .Net to Decode the message, then I try to Decrypt the EnvelopedCms.ContentInfo.Content.
This is what I try to do;
public static async Task<byte[]> DecryptCustom(string certificateId, string encryptedBase64Content)
{
var bytes = Convert.FromBase64String(encryptedBase64Content);
var contentInfo = new ContentInfo(bytes);
var envelopedCms = new EnvelopedCms(contentInfo);
envelopedCms.Decode(bytes);
// envelopedCms.Decrypt() <-- no go. Can't extract certificate from Key Vault
// My (naive) attempt to decrypt CMS content using Azure Key Vault certificates
byte[] decryptedContent;
using (var client = new KeyVaultClient(GetKeyVaultToken))
{
var decryptionresult = await client.DecryptAsync(GetKeyUrl(certificateId), "RSA1_5", envelopedCms.ContentInfo.Content);
decryptedContent = decryptionresult.Result;
}
return decryptedContent;
}
I was hoping it could be that easy, but it gives me the following error;
Unable to decrypt specified value with this key.
I read something about octets in RFC 2315, so maybe the stream (byte-array) needs some reordering before I decrypt. Do I need to unwrap some symmetric key to decrypt the real payload? I'm on thin ice here.
I'm not a cryptography professional so I might have missed something obvious, too. I was hoping someone knew what to do in this case as I really want to keep my certificates inside the Key Vault (HSM)
CMS envelope contents are encrypted using a session key, and this key is encrypted with each recipients (there can be many) public key before transmission.
What you need is to extract your recipient's encrypted session key, and unwrap it with the private key stored in key vault. I'm not near Visual Studio right now, but here is the pseudocode:
// Extract the first (and often only) receiver's encrypted session key
var key = envelopedCms.Receivers[0].EncryptionKey;
// Unwrap the sessionKey using the receiver's private key stored in key vault:
var sessionKey = (await keyVaultClient.Unwrap(uri, "certificatename", key)).Result;
Finally, using the sessionKey, you can decrypt the envelope contents (ContentInfo.Content). The encryption type is specified in the envelope's encryption algorithm-property.
Has anyone had luck decrypting a .NET forms authentication generated cookie in node js using the crypto library?
I'm using AES for encryption and SHA1 for validation in .NET forms authentication mechanism.
I was wondering if someone had this problem before and solved it?
I played with this and made the following code for Node.js:
var crypto = require('crypto');
var iconv = require('iconv-lite');
function hash(password) {
// your validation key from web.config + 00 at the end
var buf = new Buffer('...00', 'hex');
var hmac = crypto.createHmac('sha1', buf);
// convert password from utf8 to UTF16
var pass = iconv.encode(password, 'utf16le');
hmac.update(pass);
return hmac.digest('base64');
}
console.log(hash('test'));
Use it to compare hashes from your database.
It depends on web.config settings.
You can see the source code of Encryp and Decrypt here with all the different possibilities (Framework20SP1, Framework20SP2, etc)
https://github.com/Microsoft/referencesource/blob/master/System.Web/Security/FormsAuthentication.cs
For example, on Framework20SP1 the cookie looks like: Enc(IV+SerializedTicket+Modifier)+HMAC(Enc(IV+SerializedTicket+Modifier))