001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2015 Richard "Shred" Körber
005 *   http://acme4j.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.acme4j.challenge;
015
016import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode;
017
018import java.security.PublicKey;
019
020import org.jose4j.jwk.PublicJsonWebKey;
021import org.jose4j.lang.JoseException;
022import org.shredzone.acme4j.Session;
023import org.shredzone.acme4j.exception.AcmeProtocolException;
024import org.shredzone.acme4j.toolbox.JSONBuilder;
025
026/**
027 * An extension of {@link Challenge} that handles challenges with a {@code token} and
028 * {@code keyAuthorization}.
029 */
030public class TokenChallenge extends Challenge {
031    private static final long serialVersionUID = 1634133407432681800L;
032
033    protected static final String KEY_TOKEN = "token";
034    protected static final String KEY_KEY_AUTHORIZATION = "keyAuthorization";
035
036    private String authorization;
037
038    /**
039     * Creates a new generic {@link TokenChallenge} object.
040     *
041     * @param session
042     *            {@link Session} to bind to.
043     */
044    public TokenChallenge(Session session) {
045        super(session);
046    }
047
048    @Override
049    protected void respond(JSONBuilder cb) {
050        super.respond(cb);
051        cb.put(KEY_TOKEN, getToken());
052        cb.put(KEY_KEY_AUTHORIZATION, getAuthorization());
053    }
054
055    /**
056     * Gets the token.
057     */
058    protected String getToken() {
059        return getJSON().get(KEY_TOKEN).required().asString();
060    }
061
062    /**
063     * Gets the authorization.
064     */
065    protected String getAuthorization() {
066        return authorization;
067    }
068
069    /**
070     * Computes the authorization string.
071     * <p>
072     * The default is {@code token + '.' + base64url(jwkThumbprint)}. Subclasses may
073     * override this method if a different algorithm is used.
074     *
075     * @return Authorization string
076     */
077    protected String computeAuthorization() {
078        try {
079            PublicKey pk = getSession().getKeyPair().getPublic();
080            PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(pk);
081            return getToken()
082                        + '.'
083                        + base64UrlEncode(jwk.calculateThumbprint("SHA-256"));
084        } catch (JoseException ex) {
085            throw new AcmeProtocolException("Cannot compute key thumbprint", ex);
086        }
087    }
088
089    @Override
090    protected void authorize() {
091        super.authorize();
092        authorization = computeAuthorization();
093    }
094
095}