Keycloak public key format - keycloak

When I access the metadata of my realm of Keycloak via endpoint https://my-keycloak-host.com/auth/realms/my-realm, I can get the "public_key":
{
"realm": "my-realm",
"public_key": "MIIBIjANBg...",
"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
}
When I use that public_key to parse the JWT token issued by keycloak in my microservice it fails, it looks like I need to transform this public key to a format that starts with string -----BEGIN PUBLIC KEY----- and ends with -----END PUBLIC KEY-----.
I wonder what is the current format of the keycloak public_key & How can I transform the keycloak public key to the that format with "BEGIN" and "END" strings?

That API's public_key is single long string.
You just to break multiple lines by 64 size
then add -----BEGIN PUBLIC KEY-----
result of API call string , to break every 64 bytes with new lines.
and add -----END PUBLIC KEY-----
It will convert into PEM(Privacy-Enhanced Mail) format
I made a simple python program.
You needs to change the URL
import requests
import json
def convert_public_key(text):
line_length = 64
lines = []
for i in range(0, len(text), line_length):
lines.append(text[i:i+line_length] + '\n')
return '-----BEGIN PUBLIC KEY-----\n' + \
''.join(lines) + \
'-----END PUBLIC KEY-----\n'
REALM_URL='http://localhost:8180/auth/realms/test'
response = requests.get(REALM_URL)
data = json.loads(response.content.decode('utf-8'))
print(convert_public_key(data['public_key']))
This is terminal output
$ python get-public.py
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgeVGoYuRpoVttadu+ecg
mpzXiHsEF/9JbLZnxBIiBkdZAUICMbCp2EV+Q+R1HUcjaUs5ZNKVLqbINa0kMRaZ
B9/B8DidkPgMQx4iFg2inwKV8ZdAkbAuXltmfla1CwwoZlT+x/YTM7f5XoJlGkNU
XRF908ubFMlR8jTnw6w220nlduzAsz5Da1b4wyhmWXgCokGbSta7HXJD9O3z8Hf7
MfhLwLA9KTscCGLpoZSh94APX9Agr/8GE14GbpdRDTrhi2oBmdi/9hnSbXUms4ew
bxQGvuo0JDznLTCGeYempqSSE9vg5OrGPWxTT6cOs60C/KdmxqW8aVBYJOe7a2Os
uwIDAQAB
-----END PUBLIC KEY-----
It is as same as Keycloak UI.
You can find this location.
{your-realm}/Realms Setting/Keys/RS256 RSA Public Key button
You can verity public key is correct from this web site.
https://russelldavies.github.io/jwk-creator/
This is setting steps
public Key Use: Signing
Algorithm : RS256
Key ID: copy from UI kid value
Use the public key by python program (upper text)
Click this button
Get JWK - it should be matched Keycloak Certificate API - attached end of image
API response JSON should be match JWT (red circle number 6) content.
http://localhost:8180/auth/realms/test/protocol/openid-connect/certs
"n" is public key modules
"e" is public key exponent
This public key can verify JWT

Related

Using spring-security-oauth2 Authorization Server with kid and JWKS?

Following the documentation here and there, I managed to setup an Authorization Server that gives out JWT access tokens signed with asymmetric key, which are verified locally by a Resource Server using a local copy of the public key. So far so good.
My final goal is for Resource Servers to use the JWKS endpoint on the Authorization Server, and use the 'kid' header in the JWT to lookup the right key in the JWKS and verify locally, supporting key rotation.
I've found how to make the Authorization Server expose a JWKS endpoint, and also how to specify the key-set-uri for the resource server.
However, it seems that there is no way to
publish kid (key id) values in the JWKS
include the kid header in the JWT
Is there a way to do this?
I found a way to set the kid in jwks endpoint:
#FrameworkEndpoint
public class JwkSetEndpoint {
private final KeyPair keyPair;
public JwkSetEndpoint(KeyPair keyPair) {
this.keyPair = keyPair;
}
#GetMapping("/.well-known/jwks.json")
#ResponseBody
public Map<String, Object> getKey() {
RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
RSAKey key = new RSAKey.Builder(publicKey)
.keyID("YOUR_KID_HERE")
.keyUse(KeyUse.SIGNATURE).build();
return new JWKSet(key).toJSONObject();
}
}
What I did not find was a way to set it in the header of JWT.
While having the same problem I stumbled upon this post. So i hope it will be useful to someone. I do not think this is the best solution, so maybe some one comes up with a better answer i hope like setting some external bean for example.
Background:
The Jwk store is comparing the KID in the token header with the one in memory if not available it will request the well-known endpoint
So putting the KID in the JwkSetEndpoint will result in a json file with the kid inside.
next to this you need to get the KID on the header of the jwt token.
my solution in my class which extends JwtAccessTokenConverter
#Override
protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String content = null;
try {
content = objectMapper.formatMap(getAccessTokenConverter().convertAccessToken(accessToken, authentication));
} catch (Exception e) {
throw new IllegalStateException("Cannot convert access token to JSON", e);
}
Map<String, String> headers = getJwtHeader();
String token = JwtHelper.encode(content, signer, headers).getEncoded();
return token;
}
next to the KID header the Tokenstore expects a use header set to signing.
i also had to override the signer object because i got stuck with a hmac signer instead of the desired RsaSigner.

How to sign with Bouncy Castle using RSA with SHA1 and ISO9796-2 scheme 2?

I need to sign the SHA1 hash of a text with RSA and ISO9796-2 scheme 2 padding
Initially I was doing it only with SHA1 with RSA like this:
public static byte[] signer(byte[] data, PrivateKey key) throws Exception {
Signature signer = Signature.getInstance("SHA1WithRSA", "BC");
signer.initSign(key);
signer.update(data);
return signer.sign();
}
How should I modify the function? It would be easy to just replace "SHA1WithRSA" with another scheme that does what I need but I don't know if it's possible.
I solved this with this code:
public static byte[] signer(byte[] data, PrivateKey key) throws Exception {
Signature signer = Signature.getInstance("SHA1withRSA/ISO9796-2", "BC");
signer.initSign(key);
signer.update(data);
return signer.sign();
}
SHA1withRSA/ISO9796-2 does the trick.
I'm thankful to David from bouncy castle mailing list for this answer.

IOS: Decrypting a message with public key received from webservice

I'm new to this topic so sorry if this is a stupid question :\
I'm trying to decrypt a message with a given public key. Both the message and public key is given from the webservice.
See the following code for how i currently do the decrypting:
for (NSValue *refVal in keyRefs) {
SecKeyRef p_key = NULL;
[refVal getValue:&p_key];
if (p_key == NULL) continue;
size_t dataLength = encryptedData.length;
size_t outPutLength = MAX(dataLength, SecKeyGetBlockSize(p_key));
void *outPutBuf = malloc(outPutLength);
if (outPutBuf) {
// Error handling
OSStatus status = SecKeyDecrypt(p_key,
kSecPaddingNone,
encryptedData.bytes,
encryptedData.length,
outPutBuf,
&outPutLength
);
NSLog(#"decryption result code: %ld (size: %lu)", status, outPutLength);
NSLog(#"FINAL decrypted text: %s", outPutBuf);
if (status == errSecSuccess) {
break;
}
} else {
//Error handling
}
}
I get no errors, but the decrypted string is displayed like this (the correct output should be a JSON array):
decryption result code: 0 size:511)
FINAL decrypted text: ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ
Is it because I use the "SecKeyDecrypt" with a "public key" instead of a "private key"? In that case, what should i instead use for decrypting?
Thanks for any help regarding this!
EDIT: I'm using code from: http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ to use the public key i get from the server (this is where "keyRefs" from the code snippet comes from)
Of course, the public key is something someone else uses to encrypt data so that only someone with the private key can decrypt it.
The very definition of a public key is that you can give it to anyone. You wouldn't want anyone to be able to decrypt anyone else's encrypted message would you?
It is impossible to tell from your code fragment where your private key is stored, or what the contents (or even class) of keyRefs is.
EDIT: In response to above OP's comment. And clarification.
*"The public key itself is the public part of a RSA-key-pair stored on the server. The encrypted message was created on the server by first JSON-encoding the object, then encrypted with the private-key with OPENSSL_PKCS1_PADDING, then base64-encoded, and then JSON-encoded again as a part of the final message. The message and public key is stored on the client. What i want is to decrypt the message on the client by using the public key. As i said, im not very good at this subject so i might have tried to do this the wrong way"*
Thats not how public key cryptography works. The server and client exchange public keys. Then each of them use the other's public key to encrypt data sent to the opposite party. The receiving party always uses their own private key to decrypt the message.
If you want the server to generate an encrypted response, have the client pass their public key in the request, use that public key to encrypt the response, and then decrypt the response on the client with the client's private key.

supply public key in CERTENROLL request

I want to request a cert (from AD cert server) using a template. I want to supply the public key in the request. Using msft's SDK sample
IX509CertificateRequest iRequest = objEnroll.Request;
// then get the inner PKCS10 request
IX509CertificateRequest iInnerRequest =
iRequest.GetInnerRequest(InnerRequestLevel.LevelInnermost);
IX509CertificateRequestPkcs10 iRequestPkcs10 =
iInnerRequest as IX509CertificateRequestPkcs10;
// create CX500DistinguishedName
CX500DistinguishedName objName = new CX500DistinguishedName();
objName.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
// set up the subject name
iRequestPkcs10.Subject = objName;
I think I then need to do some thing like this
iRequestPkcs10.PublicKey.InitializeFromEncodedPublicKeyInfo(xx);
but I dont know what xx is. I have the public key (In a bouncy castle PKCS10 object), but what format must it be in to pass to this function?
You can specify the public key in a number of different formats.
According to MSDN, InitializeFromEncodedPublicKeyInfo takes two parameters: the first is the public key, and the second is an EncodingType enumeration value that specifies the format of the public key you are supplying.

How to get public key from a certificate in iPhone program?

Now I have a certificate which includes a public key, I want to get the public key from it in iPhone SDK programming, and encrypt a string by RSA with the public key. What should I do?
SecKeyRef publicKeyReference = NULL;
NSMutableDictionary* queryPublicKey;
SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference);
publicKeyReference - is your public key representation;
queryPublicKey - is your certificate representation;
SecItemCopyMatching - function that allows you to retrieve public key from certificate!