12

I need to convert a RSA PublicKey into a valid JWK. Especially the values "n" an "e" of the JWK are the ones I'm struggling with. The encoding does not seem to be correct, when looking at example JWK's at https://www.rfc-editor.org/rfc/rfc7517#page-25

Currently my code looks basically like this:

private Map<String, Object> generateJWK(PublicKey publicKey){

    RSAPublicKey rsa = (RSAPublicKey) publicKey;

    Map<String, Object> values = new HashMap<>();

    values.put("kty", rsa.getAlgorithm()); // getAlgorithm() returns kty not algorithm
    values.put("kid", "someuniqueid");
    values.put("n", Base64.encode(rsa.getModulus().toString()));
    values.put("e", Base64.encode(rsa.getPublicExponent().toString()));
    values.put("alg", "RS256");
    values.put("use", "sig");

    return values;
}

The output however does not seem to be correctly encoded or something, e for example looks like this: NjU1Mzc=

n does not include special characters like -, _ and + . :

jMzk1MNT0xTk2NED1xzzgNyQ00IykADzMAM0c0wz0M0MONj2z5TgNzI3yAM0OONYzzMjzwNzDxgAzxDxzMMAjTwNNDYMINMgNQDOEAkIM2jMQzkjUTDUYONNg1A00Tw1Nx4YEzAzjUT1MTNMjDjMM1MNNAjyTMIzxNADDINQANwT5yTDEMjEzNz2z2gOgjDDDNyNDjTzz43ETOYMI35gDjE00MYYM2DzDjDgww53Mwz0ME1NMgOM3MIzYTzMwzOMIQU5MjOzUjMNQNNg50U5NIDNzw2DMMOggNcQQM21TI5NMzDTN5Mj123O33MNNMkyNTNONxMM5wMMc04jTgAUE3MM1zMg4NNMT4MNDMM5yTO2j4jNDEMy1yNANNAzOIEUDzNwzExwTIkNjUjkN54Uz0DT5x0zM51k2MxYkx0zMNzxMkDUDTTQN3gAYODATQDDwMDMjMMcONjxMNTYMT5kgxNkMjNMQU0jzMEwIIMzTzUD4MgYDkDNzcAzN0TN4yNTz11DMxDUjDM2MyDMy4DEINMwT22QxjNNEzNDATy1OM1NNDxYgz5TxDkj3gQ32kIwNNkDO3xczDAENcTMNO0MOjTDwE3g11wNUcgNTwQk30kjjNNzTz4jTj4OOjQNYzMzcMjTQMkyzNNNUQOTOMMkMMMNzwNxDOEkg4xADIT4DNxMz2TENT4yN4z2I2zjyMU3DTOEQN4MIQjNDMU5Y11QkccwMNI0kNzyNjMMN4NMTTNMzMwxMjjDzgAANO1zwjYIEUjM1ADgDNjxTITMNNkIYxzyzzEEDMzDzNjzM4NjNNjc3ITTD0T5jzN=

Am I assuming right that both values n and e are not properly encoded? How should I convert the PublicKey to JWK? (Can not use 3rd party libraries)

Community
  • 1
  • 1
KevKosDev
  • 811
  • 2
  • 9
  • 31

2 Answers2

18

JWK uses base64url encoding which is slighly different to base64. Additionally, do not use toString() method on BigInteger values. Get directly the data as byte array

Change

Base64.encode(rsa.getModulus().toString())
Base64.encode(rsa.getPublicExponent().toString())

To

Base64.getUrlEncoder().encodeToString(rsa.getModulus().toByteArray())
Base64.getUrlEncoder().encodeToString(rsa.getPublicExponent().toByteArray())
pedrofb
  • 37,271
  • 5
  • 94
  • 142
0

code below works fine, use Apache Base64, search by phrases

RFC 4648 vs RFC 2045 codec difference

and

java biginteger to byte array leading zero

private static byte[] toByteArray(BigInteger bigInteger)
    {
        byte[] bytes = bigInteger.toByteArray();
        byte[] result;
        if ((bigInteger.bitLength() % 8 == 0) && (bytes[0] == 0) && bytes.length > 1)
        {
            result = new byte[bytes.length - 1];
            System.arraycopy(bytes, 1, result, 0, result.length);
        }
        else {result = bytes;}
        return result;
    }
...
import org.apache.commons.codec.binary.Base64;
...
jwk.put("n", Base64.encodeBase64URLSafeString(toByteArray(publicKey.getModulus()) ) );
jwk.put("e", Base64.encodeBase64URLSafeString(publicKey.getPublicExponent().toByteArray()) );