I'm using IdentiyServer4 to generate tokens, I'm using the AddDeveloperSigningCredential() method to generate my RSA key with a KeyId.
But, in production, I'm using AddSigningCredential(CreateSigningCredential()), to generate a key like this :
private SigningCredentials CreateSigningCredential()
{
var signinkey = new RsaSecurityKey(RSA.Create());
signinkey.KeyId = "abcdefghijklmnopqrstuvwxyz";//How to generate KeyId ??
var credentials = new SigningCredentials(signinkey,
SecurityAlgorithms.RsaSha256);
return credentials;
}
How can I generate a KeyId? Can I set it to any arbitrary value?
You don't need to set the keyId and also creating the RSA key youself in code, sounds like bad practice. Then you can just as well use the AddDeveloperSigningCredential method.
You can actually look at the source for that method here to see how they do it in code:
https://github.com/DuendeSoftware/IdentityServer/blob/main/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs
But, in production you should generate the key externally and pass it in to IdentityServer, so the key is the same across redeployment/restarts. Otherwise previously issued tokens will not be valid anymore.
You can for example store the key in Azure Key Vault, or using some other configuration/secret system. Or in a database or as a file somewhere.
If you want to create one manually, using OpenSSL, then you can write
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes256 -out rsa-private-key.pem
Related
I am trying to fetch some certficates from hashicorp vault using tf data source
This is how cert path looks like in vault
serverA:
dev-cert: <base64 encoded cert>
qa-cert: <base64 encoded cert>
test-cert: <base64 encoded cert>
This cert is used in other resource block which works fine as shown below
resource <somegcpresource> <xyz>
{
certificate = base64decode(data.vault_generic_secret.server_cryptoobjects.data["dev-cert"])
}
Can I use a custom env variable to fetch value of certificate like;
certificate = base64decode(data.vault_generic_secret.server_cryptoobjects.data["var.env-cert"])
or a local var to reference the key name from vault datasource like;
certificate = base64decode(data.vault_generic_secret.server_cryptoobjects.data[local.certname])
Yes, the data.vault_generic_secret.server_cryptoobjects.data is an object so you can access its values with their corresponding keys. If you declare a variable env-cert:
variable "env-cert" {
type = string
default = "dev-cert"
}
then you can use it as the key:
certificate = base64decode(data.vault_generic_secret.server_cryptoobjects.data["var.env-cert"])
Yes you can also use a local as the key:
locals {
certname = "dev-cert"
}
certificate = base64decode(data.vault_generic_secret.server_cryptoobjects.data[local.certname])
I would also suggest looking at the Vault PKI secrets engine for your overall use case if you have not already, since this example in the question is using the KV2 secrets engine.
I'm developing an external OAuth provider to be able to use our company login mechanism to let employees and customers connect to snowflakes.
Snowflake setup:
First, I created a database for the sake of testing :
use role accountadmin;
create database fred_ica_db;
create warehouse fred_ica_warehouse;
create role fred_ica_role;
create user fred_ica_user password = '******' login_name = 'upn';
grant usage on database fred_ica_db to role fred_ica_role;
grant usage on warehouse fred_ica_warehouse to role fred_ica_role;
grant role fred_ica_role to user fred_ica_user;
alter user fred_ica_user set DEFAULT_WAREHOUSE='FRED_ICA_WAREHOUSE', DEFAULT_ROLE='FRED_ICA_ROLE' , DEFAULT_NAMESPACE='FRED_ICA_DB.PUBLIC';
and created the security integration object in the snowflake database like this :
create or replace security integration fred_oauth_integration
type = external_oauth
enabled = true
external_oauth_type = custom
external_oauth_issuer = 'http://$(some_endpoint)/snowflakeAuth/accessToken'
external_oauth_jws_keys_url = 'https://$(aws_account).s3.us-west-2.amazonaws.com/$(public_key_filename)'
external_oauth_audience_list = ('https://xxxx.us-east-1.snowflakecomputing.com')
external_oauth_scope_mapping_attribute = 'scp'
external_oauth_token_user_mapping_claim='upn'
external_oauth_snowflake_user_mapping_attribute='login_name';
First attempt :
I created private/public key pair to handle token signature and uploaded the public key to AWS S3, which will also be our production target for this external OAuth provider. The file is currently available for grand public (checked on a public device). The public key is in the form of :
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKC...
...4oQIDAQAB
-----END RSA PUBLIC KEY-----
My OAuth server generates some valid JWT token (verified on jwt.io) + signature verified with public key :
(Base 64 url encoded)
eyJ0eXAi...wPgXvOtg
and decoded :
{
"typ": "JWT",
"alg": "RS256"
}
{
"aud": "https://xxxx.us-east-1.snowflakecomputing.com/",
"scp": [
"session:role:fred_ica_role"
],
"iss": "http://$(some_endpoint)/snowflakeAuth/accessToken",
"exp": 1632483169,
"iat": 1632475969
}
{
...signature...
}
when executing :
select SYSTEM$VERIFY_EXTERNAL_OAUTH_TOKEN('eyJ0eXAi...wPgXvOtg');
with some token created by the system
The system returns the error :
Token Validation finished.{"Validation Result":"Failed","Failure Reason":"EXTERNAL_OAUTH_JWS_CANT_RETRIEVE_PUBLIC_KEY"}
Second attempt
(after #Srinath Menon his feedback)
From the public key, I created a json web key(JWK). I created it using java code, and when I validated, I noticed it was also possible to use an online tool to do so.
https://8gwifi.org/jwkconvertfunctions.jsp
I uploaded the result as a file on AWS S3 and made sure it is grand publicly available and that the content-type is application/json :
JWKS :
{"keys":[{"kty":"RSA","e":"AQAB","kid":"5979064a-e202-4321-90ad-8a51329aad61","n":"2636yeJSQqyO5AegaIu6vEHg6w-MzGy1nPy2qj0zJfbDw6O0ATGkG_ibdfWqMHxU-JEBV1wiThv8_Mk67cst4W5U-fg-Miy8SDxYZtIkWFMAeVbamjpi_8BChCIJRVXvS6ZQofGgmFHlJAnWszL0hID8IaHigpBLTNEVHKuPeVGTYg3RcoWTjjB3WtaIj8XqKpQY47EUOiHOa3DZISXteYlfZu9yhQdB-7s0kQhdzk-RiQIHsLfnn-ksVzntmmwHVE5KVllMJt-23bH8c1TbtrMzWHflY85K3iSjhB3EHyeqhZnKuhAR5WVxPxkGIdr9qku1Q6L489R1wRkE2Rk4oQ"}]}
JWK :
{"kty":"RSA","e":"AQAB","kid":"5979064a-e202-4321-90ad-8a51329aad61","n":"2636yeJSQqyO5AegaIu6vEHg6w-MzGy1nPy2qj0zJfbDw6O0ATGkG_ibdfWqMHxU-JEBV1wiThv8_Mk67cst4W5U-fg-Miy8SDxYZtIkWFMAeVbamjpi_8BChCIJRVXvS6ZQofGgmFHlJAnWszL0hID8IaHigpBLTNEVHKuPeVGTYg3RcoWTjjB3WtaIj8XqKpQY47EUOiHOa3DZISXteYlfZu9yhQdB-7s0kQhdzk-RiQIHsLfnn-ksVzntmmwHVE5KVllMJt-23bH8c1TbtrMzWHflY85K3iSjhB3EHyeqhZnKuhAR5WVxPxkGIdr9qku1Q6L489R1wRkE2Rk4oQ"}
Both these approaches resulted in the same error :
Token Validation finished.{"Validation Result":"Failed","Failure Reason":"EXTERNAL_OAUTH_JWS_CANT_RETRIEVE_PUBLIC_KEY"}
Got it finally working but changed my solution a little bit :
Created a key pair using commands (under MacOS) :
openssl genrsa -out snowflakeExternalOAuth.pem 2048
openssl rsa -in snowflakeExternalOAuth.pem -pubout > snowflakeExternalOAuth.pub
I extracted the public key content (removed HEADER, FOOTER and new lines) to initialise property external_oauth_rsa_public_key
I defined the security integration like this :
create or replace security integration fred_oauth_integration
type = external_oauth
enabled = true
external_oauth_type = custom
external_oauth_issuer = 'http://$(some_endpoint)/snowflakeAuth/accessToken'
external_oauth_rsa_public_key = 'MIIBIjAN...hQIDAQAB'
external_oauth_audience_list = ('https://xxxx.us-east-1.snowflakecomputing.com')
external_oauth_scope_mapping_attribute = 'scp'
external_oauth_token_user_mapping_claim='upn'
external_oauth_snowflake_user_mapping_attribute='login_name';
I also noticed that I forgot to add some 'upn' claim in my token with the username previously defined ('fred_ica_user').
After those different changes, the system is working.
Hope this will be helpful.
I have no expertise with cryptography, i'm trying to decrypt a JWE payload in ASP.net core C#, the information that i have is the encryption was performed with RSA-OAEP-256 and A256GCM, so i'm using Jose libraries do deal with cryptography, the code is like:
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.rb3as5DV3PjfYM9cmxO1uE_OaLBt_gFMG9oe1iW_8999KNXEcxAuENaAJgD0fIWdrYD0UwDfkPxzG2cKy3SkvhQE5af7HNDuJBK7p_jjWZMNxtLO3IWCVbcYmH8r-TJEf4MPrDQi_0wP0QLIqH9aR3EVzRdkZ0tn1f8uSsaRlv7rXX_09mPME8V3y7dIDVAKJ0upvJvuyjnsNuAxXVAKvXjNdesKE_GMc3P5ZH6tOBb78a3uLyTOVf-itdR0f2Y7932QPnWbOM1d45mYARUK8Q28IHlKnyhntkA9YP8HQfvFdyMnAKhzPiybSU3Hg1U6wOh6qeYOqN4IIUedzZz__HFZZLpfPJHUacEXfgOWlPO3ZryMxN9v13pDG223oHl-9zbCOdtNsU3igOWlDM4094MECfpYWWem9cuEnbADjsyqVaq0l3CoL8MLUvDKpEjgI7ezopX0UFOlTWXjDxK-EmWeHOJlUAOjRJKUtmJBhiqtjY4quWsTO5xRzX-_fPyFw2Cql2T2_03FDNgZY6u8GpvmBR3f9E1jXqzNI36vbIdtBa6l0tksOPBnLeG070boMdViKMdUxAjzCW1EcUvaXo_vgOZ-ZU8_loOvGBOYmzE6myesQCMDaHXrSJROoR-G5lk5OO-hED6KWUptkqKI5vAuAHGuojq0_1d2YT4X4e8._jpZ8zc1XWIEFVmu.TKbdl02R_2q6-QwtW4cnUiNDoXhFX_fkTDxrhxofM8BhLTMhMk1cx3jvSPJADftBd5NgHghOouZePw_NwapFpNUwgFq__QvPVKcw6QQwU1IKcuh-ShfSJRtr9oVC93zQprZSvgLJsf5K-46sbjnEBc6dzCsL07r3M9fbg4XwaJyRW7iriLTXFbFEd-CbsqlGgZupHGUbn0W_h5PVYTuwAp4I7wyp_k2xF0i9MQRcLk04fm--zdwYo7-FfM3yfsz129OXzpH7tp6c_dl-5D53_cleLFLAIn1BNnDwVFfKIPkjbjz8_NzGoXWWESW8DP00Z2Of4qVgdhtBWunduriM7NyDWaiDWgePt3qX0A3nvSp6bIp9sXdZYiuGVM3z7-iMb-oJPsj1Abx5MuRHpbvovNLHY89QnV8Qf43TCUOGxpgiNcULd4QxeLn2NmXcmbxlFVxhGHxq44XHF0A14dEiWSYp4001T7lun-yv3DPZuiGiO-L4iKHZT-gnHAzjaDIyXVo5JsL1qWGVi59DJneQrnyjodZs7ESCUIbOASJ5SJ9QtSOu-uyvKVdyK6Tfw9-cGNXAzDBAOBmHcnxMOtWcZ6AscDJhpI_2quoktpTD9GkEM0gItKZ5W3r99i8t_RoZCakBdBY6D5gwVg4yFsJyb1YvqsYsH1X8MQBPdrVBTc4DUT0M0ucpfl5bV3nlMxxEffCyk-jbNNXid9_kOjq4Xv3-pHO4qJUQpJvRYIvGqyC0Ep1swo3-gbsUIjtEfyLlf2JkcDIM5WkV_iSvEbjch9lceTMKzjQVZgMnDWPvTtEvAZCXUxbRu9wE9fuX9F2W-yJ_DyB0o871IOhdDi0VyQcipQ2wkvuZecu9uqLFGtVk5NaPxYq0uOyaDQ3QzDwucAMwalaXPua9qjhLjM6TCePgn_ncVdN5jckMXo--RRAX3o4G1Hm8_xcJ6Amcvl8vsb7hUTdXY6um6CT-WDHbi-FQgMyyn-vlC64NJdngvT3uWvcKETS8UoKhp78nFDy20qWXIqSzxwi7rf60OL7vPZ_6McVfXORTbMjxvGRTT5skgkY58B5VPvB1c6vN12tKN3E3lqbJCpzass0rSzbfBxdRDg-PIV-Nuv0S-ch9aUxA5h6cMrYBZMQuiV-RO3EoqfQSU0JNGJuGir8mQAKuI5pJwROKrn1cCAaTSrcueobXz_ppJgZdxzjh_dUzZhfUrrE7DqJgPqHbuEPju7ViB39iPLsiniDz3W1iOhQKSbFDNzLmG_yzjKv1jF90onqIyo_mLN3kVlxHyvgP4MA8405OJCR_Cqmob0djhkiRsJCfNWUuVmmq0q0YPTfFIAn_R9A9Zdlwd8Ybg88_RKegLMwySgvfWT27w2OwGMQX6kCrPxV5pWS57lgmPiflq-R1H1uHXvBDoXySx3Uzv1J2ElzvtQGVufZFBznnirsQI87cwxSz76gWiVelf4GNJzRme3O1XRPhmlMlTBWCHOASRxTo9mM964oHc5g3cIRWrzNFppTV_FkJQ6cwPFYYjFdSA3Hh1w8_RxCxyXncsunAZoniCgV6KEBN7A5KybKMu424JQqWqdlKQqHBW7XLdqMnMH-ryzie22fGmIn4RrT5ha6ZI43rvLREt_4dYJJpmh4yw1f7Bh-8mIuKd1ha3lERWP0qZhSR6sSsCHob_UTe4JW_KwVT0om4FAgMzbLoKoZf1v01QHHn0ISSH9cvtgwv8TObcleMkEYrDJ_MxsSxOKm-B7VoKFDEhtns0ybZA_QYEK40voAzhrGO4CPjsOR7tWKIUFk3veNJRxmixBeGVP7Up7KAvXk7wAhRg060MzZ0Gf4kaWi6IS-H_hWSPYbb0NO0IQRcGYFXALt58p0hO61HpUqG5gnQGiceiMnj1DVKIVAbtnUhB_rGcrePPCKT_TQIeCyO4FRjtHw73iYePZgacVEJDGOqthPlVWJpKF9IWeUu5Ngqc3j6L75bQ4uuvUGVHAORbH3bzXoxxqx4hjU2kI0KvQwGs0VC6rCCgC2GCqJmn3-W0jxir9Mw3VBMaBOOpslv-FSMLriL4UX1solBR3pZ7bCm1vGCUooaj9tG0Pp4n6dwqGz54hkJ5vgYX5xEKZMYYJxhhKomRFNzvhJtli9qe4lNLUsMHEnwcs-BX1tMi7JSOxmPm5BNT_A7qYeRj4dJh4cR926geYdQpcXHhYC9qyNXuqsLG5Qf88rPqMeQerwBmxcwGbTUh2eUpSu4dZZzWuCUiG92Fb55UBtElxZ0mDsQ6SPso6g44oaBNaBbOJ1DnigSE6n1X-yz2HyRjYKHLCpbSHz2xDpQaHSUMVuHphOlS69Wn0Mwb_HljnQU0S34HJya1-zivBIDepkmt3_9EhLy3La872n3H1IEzkaNX7cjCr77imU2lQ32G1iwbFAo4FvJfkJhYwP2xLh1Q38GeE_2vsiQlu1iFr3Fs_Wxv-hYW6rnAz1B_1MSOI0slWMQywIhqZ7dbyyWv_S-oxecVneZ4LlSW-hxCYJGtrsGJtnEaRHVYZJ-N-MjwBm9nJU4P8mY9Nz9MPbWbovb8gJ5DLXFppZjReRk2A2zHTGKzav6Q0DYKI0NGmSDHJW6UbSjVzBBJe2r85NS4u5HGS_wyEhXXnMtJZdn6VBQj9AEY4Jrt2xZzCRoqWlUI46uMObvWq5qvkKCPcSLKyrHEIigbR3JmW3zzDNvc39RmiC6cbJ0Ypb-G0D4CNwdV2bemLzs29NIWms75cqMHxlkyKOqEX5UsYlGBhra_kM.vykYzq6TQ8ApQdyREGo6-g
var jwtSettings = new JwtSettings();
jwtSettings.RegisterMapper(new JwtPayloadConverter.JsonMapperJwt());
var decodedJwt = Jose.JWT.Decode(paylaodRetornoStone2, rsa, Jose.JweAlgorithm.RSA_OAEP_256, Jose.JweEncryption.A256GCM, jwtSettings);
when i try do decrypt the payload, i'm getting this error: specified padding mode is not valid for this algorithm.
the specific part of code that is throwing this exception is the:
byte[] cek = keyManagement.Unwrap(encryptedCek, key, jweAlgorithm.KeySize, header);
the implementation of RsaOaep256KeyManagement is doing this:
return Ensure.Type<RSA>(key, "RsaKeyManagement algorithm expects key to be of RSA type.").Decrypt(encryptedCek, RSAEncryptionPadding.OaepSHA256);
it seens that paddind mode is right, anyone have an idea to how fix this error?
Summary
Use CNG or Pkcs11Interop or any other alternatives to login to an HSM, search for a privatekey then pass it on to a 3rd party application for use.
The key cannot be extracted from the HSM or stored in memory.
a 3rd Party application needs to make use of a private key that is stored on a Hardware Security Module (HSM).
I have looked into two methods CNG and Pkcs11Interop.
The code needs to accomplish the following:
1-Authenticate and establish a session with the HSM
2-Search for the key
3-Pass the private key to the 3rd party using RSACryptoServiceProvider or other methods.
Important: The key cannot be accessed extracted from the HSM or access directly (by design for security purposes).
Below are the two samples I've put together for both CNG and PKCS11Interop
The Problem:
1-CNG I am struggling to authenticate (if that's possible)
2-PKCS11Interop I've been able to login, search for the key but struggling to make use of the key.
Happy to use either of the methods, and I welcome any assistance, alternative solutions or advice.
CNG Code:
This code works when authentication is disabled on HSM
Q. Is there a way to authenticate using a password , open a session prior to using the key?
CngProvider provider = new CngProvider("CNGProvider");
const string KeyName = "somekey";
key = CngKey.Open(KeyName, provider);
Console.WriteLine("found the key!");
var cngRsa = new RSACng(key);
var privateSshKey = new SshPrivateKey(cngRsa);
PKCS11Interop, I managed to authenticate, search for the key and assign it to a handle..
Q. How do i go about passing the private key onto a standard .Net Framework type AsymmetricAlgorithm? while keeping in mind it not exportable?
can it be passed to RSACryptoServiceProvider? and then onto AsymmetricAlgorithm?
using (IPkcs11Library pkcs11Library = Settings.Factories.Pkcs11LibraryFactory.LoadPkcs11Library(Settings.Factories, Settings.Pkcs11LibraryPath, Settings.AppType))
{
ISlot slot = Helpers.GetUsableSlot(pkcs11Library);
using (ISession session = slot.OpenSession(SessionType.ReadWrite))
{
//search for key
try
{
const string keyname = "somekey";
// Login as normal user
session.Login(CKU.CKU_USER, Settings.NormalUserPin);
IObjectHandle publicKeyHandle = Helpers.CreateDataObject(session);
IObjectHandle privateKeyHandle = Helpers.CreateDataObject(session);
// Prepare attribute template that defines search criteria
List<IObjectAttribute> privateKeyAttributes = new List<IObjectAttribute>();
privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY));
privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_KEY_TYPE, CKK.CKK_RSA));
privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL, keyname));
List<IObjectHandle> foundPrivateKeys = session.FindAllObjects(privateKeyAttributes);
if (foundPrivateKeys == null || foundPrivateKeys.Count != 1)
throw new Exception("Unable to find private key");
// Use found object handles
privateKeyHandle = foundPrivateKeys[0];
session.FindObjectsFinal();
// How do i go about using the privatekey handle here?
session.DestroyObject(privateKeyHandle);
session.Logout();
}
catch (Exception ex)
{
Console.WriteLine("Crypto error: " + ex.Message);
}
Console.WriteLine("done!");
System.Console.Write("[Hit Enter to Continue]");
System.Console.ReadLine();
}
}
With an HSM, by design, you cannot "Pass the private key to a 3rd party app".
You also cannot pass the key handle between processes (although this might work in some implementations - a key handle should be PKCS11 session specific).
Your 3rd party app needs to offload cryptographic operations to the HSM by using a configurable cryptography library like OpenSSL (or similar) or if it is using CNG it should allow you to configure the provider.
Q. Is there a way to authenticate using a password , open a session prior to using the key?
A.: For an app that uses CNG, you should use the CNG Key Storage Provider (KSP) from the HSM Vendor after you have configured it.
The HSM Vendor KSP will then prompt for the password or, if you configured the provider (using a utility or configuration file from the HSM vendor) to store the password/pin, it will just work.
eHSM sample code using NCryptNative:
SafeNCryptProviderHandle handle;
NCryptOpenStorageProvider(handle, "eHSM Key Storage Provider",0);
...
Q. How do i go about passing the private key onto a standard .Net Framework type AsymmetricAlgorithm? while keeping in mind it not exportable? can it be passed to RSACryptoServiceProvider? and then onto AsymmetricAlgorithm?
A.: No, you cannot pass key material and you cannot pass the raw PKCS11 handle to the CNG Framework. You either have to use the PKCS11Interop functions to perform all cryptographic operations OR you have to do everything in CNG (correctly configured as above).
To directly use the PKCS11 interface you continue calling PKCS11 functions with the key handle, ie.
// How do i go about using the privatekey handle here?
// something like this
session.SignInit(mechanism, privateKeyHandle);
session.Sign(data, signature));
I wanted to use Azure Key Vault to store key which will be used to create signature for JwtTokens generated by my API.
For Jwt token authentication I use Microsoft.AspNet.Authentication.JwtBearer and i configured it like that:
app.UseJwtBearerAuthentication(options =>
{
options.TokenValidationParameters.IssuerSigningKey = this.tokenAuthKey;
options.TokenValidationParameters.ValidAudience = this.tokenOptions.Audience;
options.TokenValidationParameters.ValidIssuer = this.tokenOptions.Issuer;
options.TokenValidationParameters.ValidateSignature = true;
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(0);
});
So, as you see, this configuration requires to provide full key with private part (IssureSigningKey property). It works fine when my key is stored unprotected in xml file, somewhere in file system and I'm just loading it during application startup. However, there is a problem when i try to store my key in Azure Key Vault (AKV). Since AKV only allows to withdraw public part of the key, Im struggling to make it all work together.
I found out that TokenValidationParameters class has delegate property that i can use to provide my own validator method which would use AKV encrypt/decrypt methods to determine if signature is valid.
The problem is I can't determine how to specify my own method that would generate signature part of JWT token, so that configuration of Microsoft.AspNet.Authentication.JwtBearer won't need private key.
My question is: is it possible somehow to specify my own method that creates signature for generated JWT token in JwtBearer configuration or during creation of token using JwtSecurityTokenHandler.CreateToken?