Creating a JWK server to servex keys in kubernetes - kubernetes

I am trying to create a JWK server in a pod (on k8s) which would serve the keys when a request for a particular kid comes in. I create the JWK using the generate function
func (s *jwkServer) startJWKServer() {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
jwk := go_jose.JSONWebKey{
Key: privateKey,
KeyID: "rand_key_id",
Algorithm: "RSA256",
}
s.jwk = &jwk
}
func (s *jwkServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
// Do some encoding and decoding to encapsulate into the response object
return s.jwk
}
I used the above generated key to sign the JWT token.
When the request for the above stated key comes in, the pod returns the key.
The problem here is that if the pod restarts before the request for the JWK comes in, then the key cannnot be found and this results in an error. How should such a scenario be handled ?
I thought about a couple of solutions here
Solution 1: Create the same key every time but then this would be a problem if someone gets access to the key.
Solution 2: A new JWK key is generated everytime the pod restarts but then I am concerned about the case where the pod restart happens a lot of times and then it adds latency to the code.
How should I handle the JWK server in kubernetes ?

Here is how we solved it
Public key is persisted somewhere ( either DB or a PV or a vault)
When the request comes in and the pod has not restarted, we would have the public key in memory, the server returns the key
When the request comes in and the pod HAS restarted, we wont have the public key in memory. But we would still have it in our persistence layer, we would serve the public key from the persistence layer.

Related

How to encrypt JWT secret code for protect it in case of APK decompilation?

I have a problem with JWT. If someone decompiles my APK, they can see the secret code I used to create my token. Example code, the code is secret:
String originalInput = "secret";
String encodedString =Base64Utils.encode(originalInput.getBytes());
String jwt = Jwts.builder().claim("emailId","test123#gmail.com").claim("user", "123456")
.claim("phoneNo", "1111111111")
.signWith(SignatureAlgorithm.HS256, encodedString)
.compact();
As there are a client (the APK) and a backend, you should use an asymmetric algorithm such as RS256, PS256 or ES256, not a symmetric one (HS256).
If the issuer of the token is your backend, you only need the public key on client side (your APK). This key can safely be shipped as it is public.
If the client is the issuer, key should not be shipped with your application but generated on the device and securely stored using the Keystore API (https://developer.android.com/training/articles/keystore). The associated public key should be sent to the backend. This means that each client has a uniquely generated private key.

Signing JWT token with per-user key rather than application-wide

Normally, JWT tokens are signed with an application-wide secret or an asymmetric key pair. However, I am integrating into a system that uses a per-user secret that is in fact the salt of the password in the Users table (called private_key there).
I find this system a bit odd. It was apparently meant to make sure that if a user changed his password, the issued tokens would stop working. But it does kill the main advantage of JWT: for any other system to be able to accept the token without having to call the Auth service to validate it. In this case, decoding the token requires to decode it without validation, fetching the database user/private key and then validating it.
The .net code:
List<Claim> claims = new List<Claim>()
{
new Claim("UserUUID", user.UserUUID.ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(user.PrivateKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["JwtIssuer"],
audience: _configuration["JwtIssuer"],
claims: claims,
expires: expirationDate,
signingCredentials: creds);
return Ok(new
{
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_on = expirationDate
});
Is this actually reasonable and if there is an issue other than validation issues, why is it bad ?
I have seen a similar question but it doesn't address whether this scheme is a good idea, bad idea or just useless burden.

Getting Keycloak's public key

I realized there are many iterations of this questions. But I can't seem to understand the answer correctly.
We have secured our rabbitmq and rest endpoints with a oauth2 spring server similar to this post. But it doesn't have all of the features we need and want. So we would like to use Keycloak. I have been successful with securing the rest endpoint by just going to the new version of spring security 5.1 and specifing the security.oauth2.resource.jwk.key-set-uri and setting the necessary dependencies and configuration.
While trying to secure the RabbitMQ, I have been running into problems checking the bearer token from the message header because the keycloak jwks endpoint isn't returning the true RSA public key.
RabbitMQ uses the CustomMessageListenerContainer to get the token from the message header and uses the DefaultTokenServices to check the token.
From my understanding, the endpoint that responds with the key is https://keycloak-server/auth/realms/my-realm/protocol/openid-connect/certs
Doing a HttpGet on this endpoint, I get a response that looks like the following
{
"keys": [{
"kid": "7JUbcl_96GNk2zNh4MAORuEz3YBuprXilmTXjm0gmRE",
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"n": "nE9gEtzZvV_XisnAY8Hung399hwBM_eykZ9J57euboEsKra8JvDmE6w7SSrk-aTVjdNpjdzOyrFd4V7tFqev1vVJu8MJGIyQlbPv07MTsgYE5EPM4DxdQ7H6_f3vQjq0hznkFvC-hyCqUhxPTXM5NgvH86OekL2C170xnd50RLWw8FbrprP2oRjgBnXMAif1Dd8kwbKKgf5m3Ou0yTVGfsCRG1_LSj6gIEFglxNHvGz0RejoQql0rGMxcW3MzCvc-inF3FCafQTrG5eWHqp5xXEeMHz0JosQ7BcT8MVp9lHT_utiazhQ1uKZEb4uoYOyy6mDDkx-wExpZkOx76bk_Yu-N25ljY18hNllnV_8gVMkX46_vcc-eN3DRZGNJ-Asd_sZrjbXbAvBbKwVxZeOTaXiUdvl8O0G5xX2xPnS_WA_1U4b_V1t28WtnX4bqGlOejW2kkjLvNrpfQ5fnvLjkl9I2B16Mbh9nS0LJD0RR-AkBsv3rKEnMyEkW9UsfgYKLFKuH32x_CXi9uyvNDas_q8WS3QvYwAGEMRO_4uICDAqupCVb1Jcs9dvd1w-tUfj5MQOXB-srnQYf5DbFENTNM1PK390dIjdLJh4k2efCJ21I1kYw2Qr9lHI4X2peTinViaoOykykJiol6LMujUcfqaZ1qPKDy_UnpAwGg9NyFU",
"e": "AQAB"
}
]
}
From my understanding, the field with key "n" is supposed to be an RSA256 key. Adding it to a RSAVerifier eventually gets an error of "Caused by: org.springframework.security.jwt.codec.InvalidBase64CharacterException: Bad Base64 input character decimal 95 in array position 2."
However, if I login to keycloak admin page and go into the realm settings-> keys and click the public key, a popup shows the public key minus the "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----" headers and footers. Hard coding this enables everything to work.
Is the key encoded?
I've tried doing a Base64Utils.decodeFromUrlSafeString and a Base64Utils.decodeFromString. The first returning something smaller and doesn't lool like the key and the later creating an Illegal argument exception Illegal base64 character 5f.
Update:
The n being returned is the modulous and e is the public exponent of the public key. But how does one get the actual key string?
The keys are also directly on https://keycloak-server/auth/realms/my-realm, in a format directly exploitable with your code:
{
"realm": "my-realm",
"public_key": "MIIBI...",
"token-service": "https://keycloak-server/auth/realms/my-realm/protocol/openid-connect",
"account-service": "https://keycloak-server/auth/realms/my-realm/account",
"tokens-not-before": 0
}
I'll found it also on:
open admin console
choose realm
choose Realm Settings
open tab 'Keys'
open tab 'active'
in the column 'Public keys' press 'Public Key
a popup with the public key appears.
There is toIntegerBytes before base64 encode, so it is not just base64 decode. Try:
BigInteger modulus = new BigInteger(1, Base64.decodeBase64("n-value-here"));
BigInteger exponent = new BigInteger(1, Base64.decodeBase64("e-value-here"));
JWKS endpoints are designed to have their keys changed over time (in a process called key rotation), so retrieving the public key as per the accepted answer is not a good idea. What you should opt for instead is to use a JWKS client. I use node-jwks-rsa, but the same creators also have a java implementation (jwks-rsa-java).

How to setup public key for verifying JWT tokens from Keycloak?

I'm writing backend microservice that receives requests from front-end which have Authorisation: Bearer ... header, with token obtained from keycloak (which is inside docker container).
I got the RSA public key to verify the signature of that token from Keys section of realm settings, but it seems that when container with keycloak restarts, it regenerates pair of keys, and my public key set in service config becomes invalid.
What is the proper way to work with RSA public key from keycloak? Is there some way to configure it to use a fixed pair of keys for realm? Are keys exported when realm exports? Or I have to get the public key from keycloak using url like http://keycloak:8080/auth/realms/:realm_name:, which I rather not to do because this adds a dependency between keycloak and backend.
You should verify the JWT token's signature based on the issuer identity server's /.well-known/jwks endpoint.
1) Query the issuer identity server's /.well-known/jwks endpoint (JWKS stands for JSON Web Key Set)
2) From the JWKS, get the JWK (JSON Web Key) with the same kid (Key ID) as the Bearer token we are verifying. To get the kid from your JWT token, first decode it using jwt.io's Debugger tool.
3) As long as identity server-issued tokens are verified with an asymmetric cryptography algorithm (e.g.: RS256), we can verify the signature with the Public Key only (so you won't need the Private Key)
4) The Public Key can be retrieved from the JWK (it is the x5c entry in the JWK JSON )
5) Verify the JWT Bearer token's signature with this Public Key.
For example, in Java you can verify it like this:
// verify JWT signature based on Access Identity's JWKS RSA public key (RS256)
try {
Jwk jwk = new UrlJwkProvider(new URL(issuer + Constants.JWKS_ENDPOINT)).get(decodedJWT.getKeyId());
final PublicKey publicKey = jwk.getPublicKey();
if (!(publicKey instanceof RSAPublicKey)) {
throw new IllegalArgumentException("Key with ID " + decodedJWT.getKeyId() + " was found in JWKS but is not a RSA-key.");
}
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(issuer)
.build(); //Reusable verifier instance
verifier.verify(bearerToken);
LOGGER.info("Token verified!");
} catch (Exception e) {
LOGGER.error(e.getMessage());
throw new InvalidAccessTokenException("JWTVerificationException - Invalid token signature.");
}

IdentityServer3 idsrv.partial cookie gets too big

After login when redirecting the user using context.AuthenticateResult = new AuthenticateResult(<destination>, subject, name, claims) the partial cookie gets so big that it contains up to 4 chunks and ends up causing "request too big" error.
The number of claims is not outrageous (in the 100 range) and I haven't been able to consistently reproduce this on other environments, even with larger number of claims. What else might be affecting the size of this cookie payload?
Running IdSrv3 2.6.1
I assume that you are using some .NET Framework clients, because all of these problems are usually connected with the Microsoft.Owin middleware, that has some encryption that causes the cookie to get this big.
The solution for you is again part of this middleware. All of your clients (using the Identity Server as authority) need to have a custom IAuthenticationSessionStore imlpementation.
This is an interface, part of Microsoft.Owin.Security.Cookies.
You need to implement it according to whatever store you want to use for it, but basically it has the following structure:
public interface IAuthenticationSessionStore
{
Task RemoveAsync(string key);
Task RenewAsync(string key, AuthenticationTicket ticket);
Task<AuthenticationTicket> RetrieveAsync(string key);
Task<string> StoreAsync(AuthenticationTicket ticket);
}
We ended up implementing a SQL Server store, for the cookies. Here is some example for Redis Implementation, and here is some other with EF DbContext, but don't feel forced to use any of those.
Lets say that you implement MyAuthenticationSessionStore : IAuthenticationSessionStore with all the values that it needs.
Then in your Owin Startup.cs when calling:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
SessionStore = new MyAuthenticationSessionStore()
CookieName = cookieName
});
By this, as the documentation for the IAuthenticationSessionStore SessionStore property says:
// An optional container in which to store the identity across requests. When used,
// only a session identifier is sent to the client. This can be used to mitigate
// potential problems with very large identities.
In your header you will have only the session identifier, and the identity itself, will be read from the Store that you have implemented