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

com.nimbusds.jose.JWSObject Maven / Gradle / Ivy

Go to download

Java library for Javascript Object Signing and Encryption (JOSE) and JSON Web Tokens (JWT)

There is a newer version: 9.48
Show newest version
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.nimbusds.jose;


import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.StandardCharset;
import net.jcip.annotations.ThreadSafe;

import java.security.Signature;
import java.text.ParseException;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;


/**
 * JSON Web Signature (JWS) secured object with
 * compact
 * serialisation.
 *
 * 

This class is thread-safe. * * @author Vladimir Dzhuvinov * @version 2024-04-20 */ @ThreadSafe public class JWSObject extends JOSEObject { private static final long serialVersionUID = 1L; /** * Enumeration of the states of a JSON Web Signature (JWS) secured * object. */ public enum State { /** * The object is not signed yet. */ UNSIGNED, /** * The JWS secured object is signed but its signature is not * verified. */ SIGNED, /** * The JWS secured object is signed and its signature was * successfully verified. */ VERIFIED } /** * The header. */ private final JWSHeader header; /** * The signing input for this JWS object. */ private final String signingInputString; /** * The signature, {@code null} if not signed. */ private Base64URL signature; /** * The JWS object state. */ private final AtomicReference state = new AtomicReference<>(); /** * Creates a new to-be-signed JSON Web Signature (JWS) object with the * specified header and payload. The initial state will be * {@link State#UNSIGNED unsigned}. * * @param header The JWS header. Must not be {@code null}. * @param payload The payload. Must not be {@code null}. */ public JWSObject(final JWSHeader header, final Payload payload) { this.header = Objects.requireNonNull(header); setPayload(Objects.requireNonNull(payload)); signingInputString = composeSigningInput(); signature = null; state.set(State.UNSIGNED); } /** * Creates a new signed JSON Web Signature (JWS) object with the * specified serialised parts. The state will be * {@link State#SIGNED signed}. * * @param firstPart The first part, corresponding to the JWS header. * Must not be {@code null}. * @param secondPart The second part, corresponding to the payload. * Must not be {@code null}. * @param thirdPart The third part, corresponding to the signature. * Must not be {@code null}. * * @throws ParseException If parsing of the serialised parts failed. */ public JWSObject(final Base64URL firstPart, final Base64URL secondPart, final Base64URL thirdPart) throws ParseException { this(firstPart, new Payload(secondPart), thirdPart); } /** * Creates a new signed JSON Web Signature (JWS) object with the * specified serialised parts and payload which can be optionally * unencoded (RFC 7797). The state will be {@link State#SIGNED signed}. * * @param firstPart The first part, corresponding to the JWS header. * Must not be {@code null}. * @param payload The payload. Must not be {@code null}. * @param thirdPart The third part, corresponding to the signature. * Must not be {@code null}. * * @throws ParseException If parsing of the serialised parts failed. */ public JWSObject(final Base64URL firstPart, final Payload payload, final Base64URL thirdPart) throws ParseException { try { this.header = JWSHeader.parse(firstPart); } catch (ParseException e) { throw new ParseException("Invalid JWS header: " + e.getMessage(), 0); } setPayload(Objects.requireNonNull(payload)); signingInputString = composeSigningInput(); if (thirdPart.toString().trim().isEmpty()) { throw new ParseException("The signature must not be empty", 0); } signature = thirdPart; state.set(State.SIGNED); // but signature not verified yet! if (getHeader().isBase64URLEncodePayload()) { setParsedParts(firstPart, payload.toBase64URL(), thirdPart); } else { setParsedParts(firstPart, new Base64URL(""), thirdPart); } } @Override public JWSHeader getHeader() { return header; } /** * Composes the signing input string from the header and payload. * * @return The signing input string. */ private String composeSigningInput() { if (header.isBase64URLEncodePayload()) { return getHeader().toBase64URL().toString() + '.' + getPayload().toBase64URL().toString(); } else { return getHeader().toBase64URL().toString() + '.' + getPayload().toString(); } } /** * Returns the signing input for this JWS object. * * @return The signing input, to be passed to a JWS signer or verifier. */ public byte[] getSigningInput() { return signingInputString.getBytes(StandardCharset.UTF_8); } /** * Returns the signature of this JWS object. * * @return The signature, {@code null} if the JWS object is not signed * yet. */ public Base64URL getSignature() { return signature; } /** * Returns the state of the JWS secured object. * * @return The state. */ public State getState() { return state.get(); } /** * Ensures the current state is {@link State#UNSIGNED unsigned}. * * @throws IllegalStateException If the current state is not unsigned. */ private void ensureUnsignedState() { if (state.get() != State.UNSIGNED) { throw new IllegalStateException("The JWS object must be in an unsigned state"); } } /** * Ensures the current state is {@link State#SIGNED signed} or * {@link State#VERIFIED verified}. * * @throws IllegalStateException If the current state is not signed or * verified. */ private void ensureSignedOrVerifiedState() { if (state.get() != State.SIGNED && state.get() != State.VERIFIED) { throw new IllegalStateException("The JWS object must be in a signed or verified state"); } } /** * Ensures the specified JWS signer supports the algorithm of this JWS * object. * * @throws JOSEException If the JWS algorithm is not supported. */ private void ensureJWSSignerSupport(final JWSSigner signer) throws JOSEException { if (! signer.supportedJWSAlgorithms().contains(getHeader().getAlgorithm())) { throw new JOSEException("The " + getHeader().getAlgorithm() + " algorithm is not allowed or supported by the JWS signer: Supported algorithms: " + signer.supportedJWSAlgorithms()); } } /** * Signs this JWS object with the specified signer. The JWS object must * be in a {@link State#UNSIGNED unsigned} state. * * @param signer The JWS signer. Must not be {@code null}. * * @throws IllegalStateException If the JWS object is not in an * {@link State#UNSIGNED unsigned state}. * @throws JOSEException If the JWS object couldn't be signed. */ public synchronized void sign(final JWSSigner signer) throws JOSEException { ensureUnsignedState(); ensureJWSSignerSupport(signer); try { signature = signer.sign(getHeader(), getSigningInput()); } catch (final ActionRequiredForJWSCompletionException e) { // Catch to enable state SIGNED update throw new ActionRequiredForJWSCompletionException( e.getMessage(), e.getTriggeringOption(), new CompletableJWSObjectSigning() { @Override public Signature getInitializedSignature() { return e.getCompletableJWSObjectSigning().getInitializedSignature(); } @Override public Base64URL complete() throws JOSEException { signature = e.getCompletableJWSObjectSigning().complete(); state.set(State.SIGNED); return signature; } } ); } catch (JOSEException e) { throw e; } catch (Exception e) { // Prevent throwing unchecked exceptions at this point, // see issue #20 throw new JOSEException(e.getMessage(), e); } state.set(State.SIGNED); } /** * Checks the signature of this JWS object with the specified verifier. * The JWS object must be in a {@link State#SIGNED signed} state. * * @param verifier The JWS verifier. Must not be {@code null}. * * @return {@code true} if the signature was successfully verified, * else {@code false}. * * @throws IllegalStateException If the JWS object is not in a * {@link State#SIGNED signed} or * {@link State#VERIFIED verified state}. * @throws JOSEException If the JWS object couldn't be * verified. */ public synchronized boolean verify(final JWSVerifier verifier) throws JOSEException { ensureSignedOrVerifiedState(); boolean verified; try { verified = verifier.verify(getHeader(), getSigningInput(), getSignature()); } catch (JOSEException e) { throw e; } catch (Exception e) { // Prevent throwing unchecked exceptions at this point, // see issue #20 throw new JOSEException(e.getMessage(), e); } if (verified) { state.set(State.VERIFIED); } return verified; } /** * Serialises this JWS object to its compact format consisting of * Base64URL-encoded parts delimited by period ('.') characters. It * must be in a {@link State#SIGNED signed} or * {@link State#VERIFIED verified} state. * *

	 * [header-base64url].[payload-base64url].[signature-base64url]
	 * 
* * @return The serialised JWS object. * * @throws IllegalStateException If the JWS object is not in a * {@link State#SIGNED signed} or * {@link State#VERIFIED verified} state. */ @Override public String serialize() { return serialize(false); } /** * Serialises this JWS object to its compact format consisting of * Base64URL-encoded parts delimited by period ('.') characters. It * must be in a {@link State#SIGNED signed} or * {@link State#VERIFIED verified} state. * * @param detachedPayload {@code true} to return a serialised object * with a detached payload compliant with RFC * 7797, {@code false} for regular JWS * serialisation. * * @return The serialised JOSE object. * * @throws IllegalStateException If the JOSE object is not in a state * that permits serialisation. */ public String serialize(final boolean detachedPayload) { ensureSignedOrVerifiedState(); if (detachedPayload) { return header.toBase64URL().toString() + '.' + '.' + signature.toString(); } return signingInputString + '.' + signature.toString(); } /** * Parses a JWS object from the specified string in compact format. The * parsed JWS object will be given a {@link State#SIGNED} state. * * @param s The JWS string to parse. Must not be {@code null}. * * @return The JWS object. * * @throws ParseException If the string couldn't be parsed to a JWS * object. */ public static JWSObject parse(final String s) throws ParseException { Base64URL[] parts = JOSEObject.split(s); if (parts.length != 3) { throw new ParseException("Unexpected number of Base64URL parts, must be three", 0); } return new JWSObject(parts[0], parts[1], parts[2]); } /** * Parses a JWS object from the specified string in compact format and * a detached payload which can be optionally unencoded (RFC 7797). The * parsed JWS object will be given a {@link State#SIGNED} state. * * @param s The JWS string to parse for a detached * payload. Must not be {@code null}. * @param detachedPayload The detached payload, optionally unencoded * (RFC 7797). Must not be {@code null}. * * @return The JWS object. * * @throws ParseException If the string couldn't be parsed to a JWS * object. */ public static JWSObject parse(final String s, final Payload detachedPayload) throws ParseException { Base64URL[] parts = JOSEObject.split(s); if (parts.length != 3) { throw new ParseException("Unexpected number of Base64URL parts, must be three", 0); } if (! parts[1].toString().isEmpty()) { throw new ParseException("The payload Base64URL part must be empty", 0); } return new JWSObject(parts[0], detachedPayload, parts[2]); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy