3

I need your help once again...

I want to access the Box API and authorize using a JWT (Json Web Token). To do so, I need to create a Assertion:
"Every JWT assertion is composed of three components, the header, the claims, and the signature.
- The header specifies the algorithm used for the JWT signature.
- The claims contain the information necessary to authenticate and provide the correct token.
- The signature is used to verify the identify of the application and is verified using the public key.

Once encoded and concatenated, the JWT assertion will look like this: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9eyJpc3MiOiJ2Z3. B2bWFvaDJjZ2ZjNGRuMzFnMWx0cmlhbmdlZCIsInN1YiI. 6IjE2ODczOTQzIiwiZXhwIjoxNDI5MDM3ODYwLCJqdGkiOiJ"

So, an RSA Keypair I had to create before and had to deposit the public key in the Box devolper application.

Now, I don't know how to create the signature. I found a solution with creating the Keypair, but since I already have this, I don't know how to modify the code.

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;

import sun.misc.BASE64Encoder;

public class MainClass {
  public static void main(String[] args) throws Exception {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(1024);
    KeyPair keyPair = kpg.genKeyPair();

    byte[] data = "test".getBytes("UTF8");

    Signature sig = Signature.getInstance("MD5WithRSA");
    sig.initSign(keyPair.getPrivate());
    sig.update(data);
    byte[] signatureBytes = sig.sign();
    System.out.println("Singature:" + new  
           BASE64Encoder().encode(signatureBytes));

    sig.initVerify(keyPair.getPublic());
    sig.update(data);

    System.out.println(sig.verify(signatureBytes));
  }
}
  • 1
    Have you considered using the official [Java SDK for Box](https://github.com/box/box-java-sdk)? It will handle the JWT authentication flow for you. If you want to write it yourself, you might check out their [JWT implementation](https://github.com/box/box-java-sdk/blob/master/src/main/java/com/box/sdk/BoxDeveloperEditionAPIConnection.java) for inspiration. – John Hoerr Oct 20 '16 at 13:18
  • Once you've generated your Key Pair, to need to store it somehow. One PEM-file for the public key and one for the private key is a common way to store it. However, when I tried to setup code similar to yours a few weeks ago, I ended up switching from MD5 with RSA to HMAC-SHA256 because the process of reading a PEM file to a Java KeyPari was so tedious and included so many libraries! – Andreas Lundgren Oct 21 '16 at 17:58

1 Answers1

5

Box Api says

RSA keypair must be in PEM format

So, you need to export your public key to PEM. Use this code (java8)

 public static String exportPublicKeyAsPem(PublicKey publicKey) throws Exception {
    StringWriter sw = new StringWriter();

    sw.write("-----BEGIN PUBLIC KEY-----\n");
    sw.write(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
    sw.write("\n-----END PUBLIC KEY-----\n");

    return sw.toString();
 }

Creating a signed JWT token can be done in this way (You can also use a library)

public static String signJWT (String header, String payload, PrivateKey privateKey) throws Exception{
    String token = 
            Base64.getUrlEncoder().encodeToString(header.getBytes())
            + "."
            +Base64.getUrlEncoder().encodeToString(payload.getBytes());

    Signature sig = Signature.getInstance("SHA256WithRSA");
    sig.initSign(privateKey);
    sig.update(token.getBytes());
    byte[] signature = sig.sign();

    return token + "." + Base64.getUrlEncoder().encodeToString(signature);
}

Using the code:

public final static void main(String argv[]) throws Exception{

    String header = "{\"alg\": \"RS256\",\"typ\": \"JWT\"}";
    String payload = "{\"sub\": \"1234567890\",\"name\": \"John Doe\"}";

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(1024);
    KeyPair keyPair = kpg.genKeyPair();

    String publicKeyPem = exportPublicKeyAsPem(keyPair.getPublic());
    String signedToken = signJWT (header, payload, keyPair.getPrivate());

    System.out.println(publicKeyPem);
    System.out.println(signedToken);
}

Finally you will need to store the key pair and load before using it. I recommend you to pregenerate the keypair using openssl as BoxApi documentation says

pedrofb
  • 37,271
  • 5
  • 94
  • 142