Programmatic access to password from Elytron credential-store - wildfly

I am using Elytron on WildFly 12 to store a datasource password encoded.
I use the following CLI commands to store the password:
/subsystem=elytron/credential-store=ds_credentials:add( \
location="credentials/csstore.jceks", \
relative-to=jboss.server.data.dir, \
credential-reference={clear-text="changeit"}, \
create=true)
/subsystem=datasources/data-source=mydatasource/:undefine-attribute(name=password)
/subsystem=elytron/credential-store=ds_credentials:add-alias(alias=db_password, \
secret-value="datasource_password_clear_text")
/subsystem=datasources/data-source=mydatasource/:write-attribute( \
name=credential-reference, \
value={store=ds_credentials, alias=db_password})
This works very well so far. Now I need a way to read this password programmatically, so I can create a PostgreSQL database dump.

I found a possibility but somehow it feels like an improper solution.
static final String DB_PASS_ALIAS = "db_password/passwordcredential/clear/";
File keystoreFile = new File(System.getProperty("jboss.server.data.dir"),
"credentials/csstore.jceks");
// Open keystore
InputStream keystoreStream = new FileInputStream(keystoreFile);
KeyStore keystore = KeyStore.getInstance("JCEKS");
keystore.load(keystoreStream, KEYSTORE_PASS.toCharArray());
// Check if password for alias is available
if (!keystore.containsAlias(DB_PASS_ALIAS)) {
throw new RuntimeException("Alias for key not found");
}
// Get password
Key key = keystore.getKey(DB_PASS_ALIAS, KEYSTORE_PASS.toCharArray());
// Decode password - remove offset from decoded string
final String password = new String(key.getEncoded(), 2, key.getEncoded().length - 2);
I am open to any better solutions.

Related

Varnish - how to check JWT signature using digest Vmod?

I have a DockerFile based on Varnish 7.0 alpine, I have a custom vcl file to handle JWT authentication. We pass the JWT as a Bearer in the header.
I am based on this example: https://feryn.eu/blog/validating-json-web-tokens-in-varnish/
set req.http.tmpPayload = regsub(req.http.x-token,"[^\.]+\.([^\.]+)\.[^\.]+$","\1");
set req.http.tmpHeader = regsub(req.http.x-token,"([^\.]+)\.[^\.]+\.[^\.]+","\1");
set req.http.tmpRequestSig = regsub(req.http.x-token,"^[^\.]+\.[^\.]+\.([^\.]+)$","\1");
set req.http.tmpCorrectSig = digest.base64url_nopad_hex(digest.hmac_sha256(std.fileread("/jwt/privateKey.pem"), req.http.tmpHeader + "." + req.http.tmpPayload));
std.log("req sign " + req.http.tmpRequestSig);
std.log("calc sign " + req.http.tmpCorrectSig);
if(req.http.tmpRequestSig != req.http.tmpCorrectSig) {
std.log("invalid signature match");
return(synth(403, "Invalid JWT signature"));
}
My problem is that tmpCorrectSig is empty, I don't know if I can load from a file, since my file contains new lines and other caracteres ?
For information, this Vmod is doing what I want: https://code.uplex.de/uplex-varnish/libvmod-crypto, but I can't install it on my Arm M1 pro architecture, I spent so much time trying...
Can I achieve what I want?
I have a valid solution that leverages the libvmod-crypto. The VCL supports both HS256 and RS256.
These are the commands I used to generated the certificates:
cd /etc/varnish
ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
I use https://jwt.io/ to generate a token and paste in the values from my certificates to encrypt the signature.
The VCL code
This is the VCL code that will extract the JWT from the token cookie:
vcl 4.1;
import blob;
import digest;
import crypto;
import std;
sub vcl_init {
new v = crypto.verifier(sha256,std.fileread("/etc/varnish/jwtRS256.key.pub"));
}
sub vcl_recv {
call jwt;
}
sub jwt {
if(req.http.cookie ~ "^([^;]+;[ ]*)*token=[^\.]+\.[^\.]+\.[^\.]+([ ]*;[^;]+)*$") {
set req.http.x-token = ";" + req.http.Cookie;
set req.http.x-token = regsuball(req.http.x-token, "; +", ";");
set req.http.x-token = regsuball(req.http.x-token, ";(token)=","; \1=");
set req.http.x-token = regsuball(req.http.x-token, ";[^ ][^;]*", "");
set req.http.x-token = regsuball(req.http.x-token, "^[; ]+|[; ]+$", "");
set req.http.tmpHeader = regsub(req.http.x-token,"token=([^\.]+)\.[^\.]+\.[^\.]+","\1");
set req.http.tmpTyp = regsub(digest.base64url_decode(req.http.tmpHeader),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1");
set req.http.tmpAlg = regsub(digest.base64url_decode(req.http.tmpHeader),{"^.*?"alg"\s*:\s*"(\w+)".*?$"},"\1");
if(req.http.tmpTyp != "JWT") {
return(synth(400, "Token is not a JWT: " + req.http.tmpHeader));
}
if(req.http.tmpAlg != "HS256" && req.http.tmpAlg != "RS256") {
return(synth(400, "Token does not use a HS256 or RS256 algorithm"));
}
set req.http.tmpPayload = regsub(req.http.x-token,"token=[^\.]+\.([^\.]+)\.[^\.]+$","\1");
set req.http.tmpRequestSig = regsub(req.http.x-token,"^[^\.]+\.[^\.]+\.([^\.]+)$","\1");
if(req.http.tempAlg == "HS256") {
set req.http.tmpCorrectSig = digest.base64url_nopad_hex(digest.hmac_sha256("SlowWebSitesSuck",req.http.tmpHeader + "." + req.http.tmpPayload));
if(req.http.tmpRequestSig != req.http.tmpCorrectSig) {
return(synth(403, "Invalid HS256 JWT signature"));
}
} else {
if (! v.update(req.http.tmpHeader + "." + req.http.tmpPayload)) {
return (synth(500, "vmod_crypto error"));
}
if (! v.valid(blob.decode(decoding=BASE64URLNOPAD, encoded=req.http.tmpRequestSig))) {
return(synth(403, "Invalid RS256 JWT signature"));
}
}
set req.http.tmpPayload = digest.base64url_decode(req.http.tmpPayload);
set req.http.X-Login = regsub(req.http.tmpPayload,{"^.*?"login"\s*:\s*(\w+).*?$"},"\1");
set req.http.X-Username = regsub(req.http.tmpPayload,{"^.*?"sub"\s*:\s*"(\w+)".*?$"},"\1");
unset req.http.tmpHeader;
unset req.http.tmpTyp;
unset req.http.tmpAlg;
unset req.http.tmpPayload;
unset req.http.tmpRequestSig;
unset req.http.tmpCorrectSig;
unset req.http.tmpPayload;
}
}
Installing libvmod-crypto
libvmod-crypto is required to use RS256, which is not supported by libvmod-digest.
Unfortunately I'm getting an error when running the ./configure script:
./configure: line 12829: syntax error: unexpected newline (expecting ")")
I'll talk to the maintainer of the VMOD and see if we can figure out someway to fix this. If this is an urgent matter, I suggest you use a non-Alpine Docker container for the time being.
Firstly, the configure error was caused by a missing -dev package, see the gitlab issue (the reference is in a comment, but I think it should be more prominent).
The main issue in the original question is that digest.hmac_sha256() can not be used to verify RS256 signatures. A JWT RS256 signature is a SHA256 hash of the subject encrypted with an RSA private key, which can then be verified by decrypting with the RSA public key and checking the signature. This is what crypto.verifier(sha256, ...) does.
In this regard, Thijs' previous answer is already correct.
Yet the code which is circulating and has been referenced here it nothing I would endorse. Among other issues, a fundamental problem is that regular expressions are used to (pretend to) parse JSON, which is simply not correct.
I use a better implementation for long, but just did not get around to publishing it. So now is the time, I guess.
I have just added VCL snippets from production code for JWT parsing and validation.
The example is used like so with the jwt directory in vcl_path:
include "jwt/jwt.vcl";
include "jwt/rsa_keys.vcl";
sub vcl_recv {
jwt.set(YOUR_JWT); # replace YOUR_JWT with an actual variable/header/function
call recv_jwt_validate;
# do things with jwt_payload.extract(".scope")
}
Here, the scope claim contains the data that we are actually interested in for further processing, if you want to use other claims, just rename .scope or add another jwt_payload.expect(CLAIM, ...) and then use jwt_payload.extract(CLAIM).
This example uses some vmods, which we developed and maintain in particular with JWT in mind, though not exclusively:
crypto (use gitlab mirror for issues) for RS signatures (mostly RS256)
frozen (use gitlab mirror for issues) for JSON parsing
Additionally, we use
re2 (use gitlab mirror for issues) to efficiently split the JWT into the three parts (header, payload, signature)
and taskvar from objvar (gitlab) for proper variables.
One could do without these two vmods (re2 could be replaced by the re vmod or even regsub and taskvar with headers), but they make the code more efficient and cleaner.
blobdigest (gitlab) is not contained in the example, but can be used to validate HS signtures (e.g. HS256).

How to convert this code to scala (using adal to generate Azure AD token)

I am currently working on a Scala code that can establish a connection to an SQL server database using AD token.
There are too little documentation on the subject online so I tries to work on it using python. Now it is working, I am looking to convert my code to Scala.
Here is the python script:
context = adal.AuthenticationContext(AUTHORITY_URL)
token = context.acquire_token_with_client_credentials("https://database.windows.net/", CLIENT_ID, CLIENT_SECRET)
access_token = token["accessToken"]
df= spark.read \
.format("com.microsoft.sqlserver.jdbc.spark") \
.option("url", URL) \
.option("dbtable", "tab1") \
.option("accessToken", access_token) \
.option("hostNameInCertificate", "*.database.windows.net") \
.load()
df.show()
Here is the Java code that you can use as a base, using the acquireToken function:
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
...
String authority = "https://login.microsoftonline.com/<org-uuid>;
ExecutorService service = Executors.newFixedThreadPool(1);
AuthenticationContext context = new AuthenticationContext(authority, true, service);
ClientCredential credential = new ClientCredential("sp-client-id", "sp-client-secret");
AuthenticationResult result = context.acquireToken("resource_id", credential, null).get();
// get token
String token = result.getAccessToken()
P.S. But really, ADAL's usage isn't recommended anymore, it's better to use MSAL instead (here is the migration guide)

SignatureDoesNotMatch with Google's serviceAccounts.signBlob API

I am trying to generated a signed URL to an object stored on Google Cloud Storage (GCS).
Attempt 1: try the API using the API Explorer
For this, I am trying to sign the blob/object as defined in the following:
GET
<expiration time>
/<bucket name>/<object/blob name>
I first tried Google's serviceAccounts.signBlob API as discussed in the following page:
https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob
A base64-encoded string.
Note, as mentioned in the API documentation on the above-linked page, I pass a base64 representation of the blob I want to sign, to the API.
The API's response has the following structure where it contains the signedBlob key:
{
"keyId": "...",
"signedBlob": "..."
}
then I generated a signed URL using the obtained signed blob as the following:
encoded_signedBlob = base64.b64encode(signedBlob)
signed_url = "https://storage.googleapis.com/{}/{}?" \
"GoogleAccessId={}&" \
"Expires={}&" \
"Signature={}".format(
bucket_name, blob_name,
service_account_email,
expiration,
encoded_signedBlob)
and when I paste that signed URL in the browser to download the blob, I get the following error:
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
</Message>
<StringToSign>GET <bucket name> <blob/object name></StringToSign>
</Error>
Attempt 2: try python libraries
Then I tried to implement it in python as the following, but still getting the same error.
# -------------
# Part 1: obtain access token using the authorization flow discussed at:
# https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials
# -------------
client_service_account = "..."
access_token = build(
serviceName='iamcredentials',
version='v1',
http=http
).projects().serviceAccounts().generateAccessToken(
name="projects/{}/serviceAccounts/{}".format(
'-',
service_account_email),
body=body
).execute()["accessToken"]
credentials = AccessTokenCredentials(access_token, "MyAgent/1.0", None)
# -------------
# Part 2: sign the blob
# -------------
service = discovery.build('iam', 'v1', credentials=credentials)
name = 'projects/.../serviceAccounts/...'
encoded = base64.b64encode(blob)
sign_blob_request_body = {"bytesToSign": encoded}
request = service.projects().serviceAccounts().signBlob(name=name, body=sign_blob_request_body)
response = request.execute()
keyId = response["keyId"]
signedBlob = response["signature"]
# -------------
# Part 3: generate signed URL
# -------------
encoded_signedBlob = base64.b64encode(signedBlob)
signed_url = "https://storage.googleapis.com/{}/{}?" \
"GoogleAccessId={}&" \
"Expires={}&" \
"Signature={}".format(
bucket_name, blob_name,
service_account_email,
expiration,
encoded_signedBlob)
You could take a look to the implementation of this using the client libraries, you could give it a try, remember to include google-cloud-storage in your requirements.txt and a service account as specified in the code sample for python

Google IOT Core and Raspberry Pi: Error: Connection Refused: Bad username or password

I followed the tutorial below to connect my raspberry pi 3 to the Google IOT Core. I setup the Google Core IOT part OK at the Google console and all steps were followed for the raspberry pi part, but, The connection is always refused as per the error messages below.
error { Error: Connection refused: Bad username or password
at MqttClient._handleConnack (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/lib/client.js:920:15)
at MqttClient._handlePacket (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/lib/client.js:350:12)
at work (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/lib/client.js:292:12)
at Writable.writable._write (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/lib/client.js:302:5)
at doWrite (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/node_modules/readable-
stream/lib/_stream_writable.js:428:64)
at writeOrBuffer (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/node_modules/readable-
stream/lib/_stream_writable.js:417:5)
at Writable.write (/home/pi/Desktop/Google-IoT-
Device/node_modules/mqtt/node_modules/readable-
stream/lib/_stream_writable.js:334:11)
at TLSSocket.ondata (_stream_readable.js:639:20)
at emitOne (events.js:116:13)
at TLSSocket.emit (events.js:211:7) code: 4 }
close
The tutorial link:
https://hub.packtpub.com/build-google-cloud-iot-application/#comment-53421
This is the top part of my index.js file:
var fs = require('fs');
var jwt = require('jsonwebtoken');
var mqtt = require('mqtt');
var rpiDhtSensor = require('rpi-dht-sensor');
var dht = new rpiDhtSensor.DHT11(2); // `2` => GPIO2
var projectId = 'nifty-*******-******';
var cloudRegion = 'us-central1';
var registryId = 'device-registry';
var deviceId = 'raspberrypi';
var mqttHost = 'mqtt.googleapis.com';
var mqttPort = 8883;
var privateKeyFile = '../certs/rsa_private.pem';
var algorithm = 'RS256';
var messageType = 'state'; // or event
var mqttClientId = 'projects/' + projectId + '/locations/' + cloudRegion +
'/registries/' + registryId + '/devices/' + deviceId;
var mqttTopic = '/devices/' + deviceId + '/' + messageType;
var connectionArgs = {
host: mqttHost,
port: mqttPort,
clientId: mqttClientId,
username: 'unused',
password: createJwt(projectId, privateKeyFile, algorithm),
protocol: 'mqtts',
secureProtocol: 'TLSv1_2_method'
};
The tutorial doesn't say anything about downloading the Google root CA certificate so I followed this tutorial:
https://raspberrypi.stackexchange.com/questions/76419/entrusted-certificates-installation
I also checked the connection route was OK by following this at Google and everything checked OK:
https://cloud.google.com/iot/docs/troubleshooting
The projectID, registryID, deviceID, and region all checked correct.
I am sure it must very simple but this has frustrated me for a week now. I have trawled the internet but what ever I tried results in the same error.
Is there anyone that can help?
Things to triple check:
Your project ID, registry and device names are all correct with correct case and dash vs. underscore
Your SSL key type matches the algorithm and specified type in the registry. I.e. if you have an RSA key, make sure it's RSA and not RSA with the x509 specified in the registry.
The root cert is correct... That tutorial you linked is WAY more complicated than it needs to be. Just run: wget https://pki.google.com/roots.pem to get the current roots.pem from Google.
Not to throw yet another tutorial at you, but I also literally just published a blog post with really detailed info on this with step-by-step, mostly because the other tutorials either had holes, or stale info.
One other note: I see you're using the state MQTT topic to send, that's right, but in the comment you have listed event. It's events. So if you try to send to event, that'll fail too.
Same problem I have faced and solved it, reduce expiry time where create json token for password
For me it was a simple oversight, namely a space between the equal sign (=) and the region name. Code was:
before (failing) ....
node cloudiot_mqtt_example_nodejs.js mqttDeviceDemo \
--projectId=myproject \
--cloudRegion= us-central1 \
--registryId=1234 \
--deviceId=test-device \
--privateKeyFile=./cert/rsa_private.pem \
--numMessages=25 \
--algorithm=RS256
after (fixed error message "error Error: Connection refused: Bad username or password) ....
--cloudRegion=us-central1 \
also, see examples here:
https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/iot/mqtt_example

grails - RestClientBuilder

I am using the current version of rest client builder plugin. I tested out the uri via curl:
curl --user username:password https://localhost:8085/rest/api/latest/plan.json?os_authType=basic
I get the expected json in return. When I try to translate this to grails using the plugin like this:
RestBuilder rb = new RestBuilder()
def response = rb.get("https://localhost:8085/rest/api/latest/plan.json?os_authType=basic"){
auth 'username', 'password'
}
response.json instanceof JSONObject
I get this error:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Why does it work in curl and not with the plugin? How do I get this to work?
Thanks!
You need to add the root certificate to the store of the trusted ones.
http://docs.oracle.com/javase/tutorial/security/toolsign/rstep2.html
Import the Certificate as a Trusted Certificate
Before you can grant the signed code permission to read a specified file, you need to import Susan's certificate as a trusted certificate in your keystore.
Suppose that you have received from Susan
the signed JAR file sCount.jar, which contains the Count.class file, and
the file Example.cer, which contains the public key certificate for the public key corresponding to the private key used to sign the JAR file.
Even though you created these files and they haven't actually been transported anywhere, you can simulate being someone other than the creater and sender, Susan. Pretend that you are now Ray. Acting as Ray, you will create a keystore named exampleraystore and will use it to import the certificate into an entry with an alias of susan.
A keystore is created whenever you use a keytool command specifying a keystore that doesn't yet exist. Thus we can create the exampleraystore and import the certificate via a single keytool command. Do the following in your command window.
Go to the directory containing the public key certificate file Example.cer. (You should actually already be there, since this lesson assumes that you stay in a single directory throughout.)
Type the following command on one line:
keytool -import -alias susan
-file Example.cer -keystore exampleraystore
Since the keystore doesn't yet exist, it will be created, and you will be prompted for a keystore password; type whatever password you want.
The keytool command will print out the certificate information and ask you to verify it, for example, by comparing the displayed certificate fingerprints with those obtained from another (trusted) source of information. (Each fingerprint is a relatively short number that uniquely and reliably identifies the certificate.) For example, in the real world you might call up Susan and ask her what the fingerprints should be. She can get the fingerprints of the Example.cer file she created by executing the command
keytool -printcert -file Example.cer
If the fingerprints she sees are the same as the ones reported to you by keytool, the certificate has not been modified in transit. In that case you let keytool proceed with placing a trusted certificate entry in the keystore. The entry contains the public key certificate data from the file Example.cer and is assigned the alias susan.
You can just disable SSL check for RestBuilder.
See an example of code:
static Scheme disableSSLCheck() {
def sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, [new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
#Override
X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0]
}
}] as TrustManager[], new SecureRandom())
def sf = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
def httpsScheme = new Scheme("https", sf, 443)
httpsScheme
}
And register this Scheme to the RestClient:
Scheme httpsScheme = disableSSLCheck()
restClient.client.connectionManager.schemeRegistry.register(httpsScheme)
Mb too late but have a look here.
https://gist.github.com/thomastaylor312/80fcb016020e4115aa64320b98fb0017
I do have it as separate method in my Integration test
def static disableSSLCheck() {
def nullTrustManager = [
checkClientTrusted: { chain, authType -> },
checkServerTrusted: { chain, authType -> },
getAcceptedIssuers: { null }
]
def nullHostnameVerifier = [
verify: { hostname, session -> true }
]
SSLContext sc = SSLContext.getInstance("SSL")
sc.init(null, [nullTrustManager as X509TrustManager] as TrustManager[], null)
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
HttpsURLConnection.setDefaultHostnameVerifier(nullHostnameVerifier as HostnameVerifier)
}
And then just
void "test authentication"(){
given:
String url = "j_spring_security_check"
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
form.add("grant_type", "password")
form.add("j_username", "vadim#ondeviceresearch.com")
form.add("j_password", "notSecure")
form.add("_spring_security_remember_me", "true")
//TODO SET username and pass
//todo get token back
disableSSLCheck()
when:
RestResponse response = rest.post(host + url){
accept("application/json")
contentType("application/x-www-form-urlencoded")
body(form)
}
response
then:
response.status == 200
}