All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.shredzone.acme4j.toolbox.JoseUtils Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2019 Richard "Shred" Körber
 *   http://acme4j.shredzone.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.shredzone.acme4j.toolbox;

import java.net.URL;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.Map;

import javax.crypto.SecretKey;

import edu.umd.cs.findbugs.annotations.Nullable;
import org.jose4j.jwk.EllipticCurveJsonWebKey;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.lang.JoseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class that takes care of all the JOSE stuff.
 * 

* Internal class, do not use in your project! The API may change anytime, in a breaking * manner, and without prior notice. * * @since 2.7 */ public final class JoseUtils { private static final Logger LOG = LoggerFactory.getLogger(JoseUtils.class); private JoseUtils() { // Utility class without constructor } /** * Creates an ACME JOSE request. * * @param url * {@link URL} of the ACME call * @param keypair * {@link KeyPair} to sign the request with * @param payload * ACME JSON payload. If {@code null}, a POST-as-GET request is generated * instead. * @param nonce * Nonce to be used. {@code null} if no nonce is to be used in the JOSE * header. * @param kid * kid to be used in the JOSE header. If {@code null}, a jwk header of the * given key is used instead. * @return JSON structure of the JOSE request, ready to be sent. */ public static JSONBuilder createJoseRequest(URL url, KeyPair keypair, @Nullable JSONBuilder payload, @Nullable String nonce, @Nullable String kid) { try { var jwk = PublicJsonWebKey.Factory.newPublicJwk(keypair.getPublic()); var jws = new JsonWebSignature(); jws.getHeaders().setObjectHeaderValue("url", url); if (kid != null) { jws.getHeaders().setObjectHeaderValue("kid", kid); } else { jws.getHeaders().setJwkHeaderValue("jwk", jwk); } if (nonce != null) { jws.getHeaders().setObjectHeaderValue("nonce", nonce); } jws.setPayload(payload != null ? payload.toString() : ""); jws.setAlgorithmHeaderValue(keyAlgorithm(jwk)); jws.setKey(keypair.getPrivate()); jws.sign(); if (LOG.isDebugEnabled()) { LOG.debug("{} {}", payload != null ? "POST" : "POST-as-GET", url); if (payload != null) { LOG.debug(" Payload: {}", payload); } LOG.debug(" JWS Header: {}", jws.getHeaders().getFullHeaderAsJsonString()); } var jb = new JSONBuilder(); jb.put("protected", jws.getHeaders().getEncodedHeader()); jb.put("payload", jws.getEncodedPayload()); jb.put("signature", jws.getEncodedSignature()); return jb; } catch (JoseException ex) { throw new IllegalArgumentException("Could not create a JOSE request", ex); } } /** * Creates a JSON structure for external account binding. * * @param kid * Key Identifier provided by the CA * @param accountKey * {@link PublicKey} of the account to register * @param macKey * {@link SecretKey} to sign the key identifier with * @param macAlgorithm * Algorithm of the MAC key * @param resource * "newAccount" resource URL * @return Created JSON structure */ public static Map createExternalAccountBinding(String kid, PublicKey accountKey, SecretKey macKey, String macAlgorithm, URL resource) { try { var keyJwk = PublicJsonWebKey.Factory.newPublicJwk(accountKey); var innerJws = new JsonWebSignature(); innerJws.setPayload(keyJwk.toJson()); innerJws.getHeaders().setObjectHeaderValue("url", resource); innerJws.getHeaders().setObjectHeaderValue("kid", kid); innerJws.setAlgorithmHeaderValue(macAlgorithm); innerJws.setKey(macKey); innerJws.setDoKeyValidation(false); innerJws.sign(); var outerClaim = new JSONBuilder(); outerClaim.put("protected", innerJws.getHeaders().getEncodedHeader()); outerClaim.put("signature", innerJws.getEncodedSignature()); outerClaim.put("payload", innerJws.getEncodedPayload()); return outerClaim.toMap(); } catch (JoseException ex) { throw new IllegalArgumentException("Could not create external account binding", ex); } } /** * Converts a {@link PublicKey} to a JOSE JWK structure. * * @param key * {@link PublicKey} to convert * @return JSON map containing the JWK structure */ public static Map publicKeyToJWK(PublicKey key) { try { return PublicJsonWebKey.Factory.newPublicJwk(key) .toParams(JsonWebKey.OutputControlLevel.PUBLIC_ONLY); } catch (JoseException ex) { throw new IllegalArgumentException("Bad public key", ex); } } /** * Converts a JOSE JWK structure to a {@link PublicKey}. * * @param jwk * Map containing a JWK structure * @return the extracted {@link PublicKey} */ public static PublicKey jwkToPublicKey(Map jwk) { try { return PublicJsonWebKey.Factory.newPublicJwk(jwk).getPublicKey(); } catch (JoseException ex) { throw new IllegalArgumentException("Bad JWK", ex); } } /** * Computes a thumbprint of the given public key. * * @param key * {@link PublicKey} to get the thumbprint of * @return Thumbprint of the key */ public static byte[] thumbprint(PublicKey key) { try { var jwk = PublicJsonWebKey.Factory.newPublicJwk(key); return jwk.calculateThumbprint("SHA-256"); } catch (JoseException ex) { throw new IllegalArgumentException("Bad public key", ex); } } /** * Analyzes the key used in the {@link JsonWebKey}, and returns the key algorithm * identifier for {@link JsonWebSignature}. * * @param jwk * {@link JsonWebKey} to analyze * @return algorithm identifier * @throws IllegalArgumentException * there is no corresponding algorithm identifier for the key */ public static String keyAlgorithm(JsonWebKey jwk) { if (jwk instanceof EllipticCurveJsonWebKey) { var ecjwk = (EllipticCurveJsonWebKey) jwk; switch (ecjwk.getCurveName()) { case "P-256": return AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256; case "P-384": return AlgorithmIdentifiers.ECDSA_USING_P384_CURVE_AND_SHA384; case "P-521": return AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512; default: throw new IllegalArgumentException("Unknown EC name " + ecjwk.getCurveName()); } } else if (jwk instanceof RsaJsonWebKey) { return AlgorithmIdentifiers.RSA_USING_SHA256; } else { throw new IllegalArgumentException("Unknown algorithm " + jwk.getAlgorithm()); } } /** * Analyzes the {@link SecretKey}, and returns the key algorithm identifier for {@link * JsonWebSignature}. * * @param macKey * {@link SecretKey} to analyze * @return algorithm identifier * @throws IllegalArgumentException * there is no corresponding algorithm identifier for the key */ public static String macKeyAlgorithm(SecretKey macKey) { if (!"HMAC".equals(macKey.getAlgorithm())) { throw new IllegalArgumentException("Bad algorithm: " + macKey.getAlgorithm()); } var size = macKey.getEncoded().length * 8; if(size < 256) { throw new IllegalArgumentException("Bad key size: " + size); } if (size >= 512) { return AlgorithmIdentifiers.HMAC_SHA512; } else if (size >= 384) { return AlgorithmIdentifiers.HMAC_SHA384; } else { return AlgorithmIdentifiers.HMAC_SHA256; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy