How to setup Gatling to use client certificates per virtual user?
I am preparing Gatling simulations for API that is protected by client certificates and currently I only managed to set using one client certificate for all simulations in Gatling configuration
ssl {
keyStore {
#type = ""
file = "C:/client_certificates/00001.pfx"
password = "password"
#algorithm = ""
}
}
However, I would like to have different client certificates for different virtual users (client certificates can be selected randomly from the list of available client certificates or circularly). Galing documentation mentions keymanagerfactory and perUserKeyManagerFactory function, but without explicit examples of how to use it.
The keys of the config provided in the question are referenced in io.gatling.core.ConfigKeys.ssl.keyStore. The values are read and passed to Ssl.newKeyManagerFactory to create a KeyManagerFactory.
perUserKeyManagerFactory takes in a function Long => KeyManagerFactory that constructs the KeyManagerFactory from the virtual user's ID.
Knowing that we can write the following:
import io.gatling.commons.util.Ssl
...
.perUserKeyManagerFactory { userId =>
val fileName = f"C:/client_certificates/$userId%05d.pfx"
Ssl.newKeyManagerFactory(None, fileName, "password", None)
}
With the f interpolator, we can easily pad the userId with zeros to have 5 digits.
To select the files circularly, we can write ${userId % totalNumber} instead of $userId.
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.
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 need to register companies on my site for an electronic procurement system. Up to now these were local companies I could meet physically and give credentials to, but now they can be companies based anywhere in the world.
The solution is to have an online registration process whereby they submit a third party certificate. So say Verisign says they are 'Company X' so I register them as Company X and issue them credentials.
How can I implement this on my site? Do I simply give them a field in the registration form where they upload their certificate file? Do I then manually check these certificates in my back office? How does one check this manually? Is there a way to automate this process?
Once they have an account, should I simply request the credentials I issue them with to log in, or can all future logins request the same certificate file? In these a particular format for certificates I can request or should I allow a number of common formats that different certificate vendors provide?
Thanks in advance.
Being able to provide a certificate does unfortunately not prove anything. A certificate is completely public, and anyone can get a hold of the SSL certificate for any website. The certificate contains a public key. Proving ownership of the corresponding private key is what's required.
This is possible to do, but it requires that your users are technical enough to know how to run scripts and/or OpenSSL terminal commands so that they can sign something with their private key. Having the users upload their private key is of course a big no-no, as it means you can now act as the user, and that would require an enormous amount of trust in you to discard the private key after you've verified it.
From a technical perspective, you can do the verification by creating some kind of challenge, for example a random string, and have the user encrypt this string with their private key. If you decrypt this string with the public key in the certificate, and get the original string back, then you know that they have possession of the corresponding private key.
Here's a self-contained Ruby script that demonstrates this, with comments indicating which part of it is run on your side, and which part is run on their side.
require "openssl"
## This happens on the client side. They generate a private key and a certificate.
## This particular certificate is not signed by a CA - it is assumed that a CA
## signature check is already done elsewhere on the user cert.
user_keypair = OpenSSL::PKey::RSA.new(2048)
user_cert = OpenSSL::X509::Certificate.new
user_cert.not_before = Time.now
user_cert.subject = OpenSSL::X509::Name.new([
["C", "NO"],
["ST", "Oslo"],
["L", "Oslo"],
["CN", "August Lilleaas"]
])
user_cert.issuer = user_cert.subject
user_cert.not_after = Time.now + 1000000000 # 40 or so years
user_cert.public_key = user_keypair.public_key
user_cert.sign(user_keypair, OpenSSL::Digest::SHA256.new)
File.open("/tmp/user-cert.crt", "w+") do |f|
f.write user_cert.to_pem
end
## This happens on your side - generate a random phrase, and agree on a digest algorithm
random_phrase = "A small brown fox"
digest = OpenSSL::Digest::SHA256.new
## The client signs (encrypts a cheksum) the random phrase
signature = user_keypair.sign(digest, random_phrase)
## On your side, verify the signature using the user's certificate.
your_user_cert = OpenSSL::X509::Certificate.new(File.new("/tmp/user-cert.crt"))
puts your_user_cert.public_key.verify(digest, signature, random_phrase + "altered")
# => falase
puts your_user_cert.public_key.verify(digest, signature, random_phrase)
# => true
## On your side - attempting to verify with another public key/keypair fails
malicious_keypair = OpenSSL::PKey::RSA.new(2048)
puts malicious_keypair.public_key.verify(digest, signature, random_phrase)
Note that this script does not take into account the CA verification step - you also obviously want to verify that the user's certificate is verified by a CA, such as Verisign that you mentioned, because anyone can issue a certificate and hold a private key for foo.com - it's the CA signature of the certificate that provides authenticity guarantees.
When inserting a smart card to a reader the certificates will be read in to the personal store, my question is simple how I then explain for WCF that it should use a specific (of these) certificate in runtime?
My WCF service is selfhosted and are communicating with the client over TCP.
I do already have a communication that uses certificate but these certificates is stated in the config files(one for service and one for client).
Now I need to switch the certificate on the client side before communicating.
You should try to find a specific set of attributes that is unique to the certificate you would like to use.
For example we use filtering like this:
/// <summary>
/// Get the certificate we need for authentication
/// </summary>
/// <returns>the certificate</returns>
/// <exception cref="InvalidOperationException">when no certificate is available</exception>
public static X509Certificate2 GetCertificate()
{
// open private certificate store of the user
X509Store store = new X509Store(StoreName.My);
store.Open(OpenFlags.ReadOnly);
// get the collection of certificates which we need
X509Certificate2Collection certColl = store.Certificates
.Find(X509FindType.FindByExtension, new X509KeyUsageExtension().Oid.Value, false)
.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.NonRepudiation, false);
store.Close();
// if more certificates match the criteria, let the user choose one
if (certColl.Count > 1)
{
certColl = X509Certificate2UI.SelectFromCollection(
certColl, "Choose certificate", "We were unable to find the correct certificate. Please choose one from the list.",
X509SelectionFlag.SingleSelection);
}
// if no certificate is available, fail
if (certColl.Count < 1)
{
throw new InvalidOperationException("No certificates selected");
}
else
{
return certColl[0];
}
}
Check the attributes of the certificates you need and try to create your own Find criteria. Of course the use could have additional certificates in his store which match the criteria, this case a dialog window pops up and asks the user to do this himself.
Of course once the user chooses the correct certificate you can store the CN of the certificate in the application configuration file so next time he won't have to do these steps again.