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

net.aholbrook.paseto.Paseto Maven / Gradle / Ivy

package net.aholbrook.paseto;

import net.aholbrook.paseto.base64.Base64Provider;
import net.aholbrook.paseto.crypto.KeyPair;
import net.aholbrook.paseto.crypto.NonceGenerator;
import net.aholbrook.paseto.encoding.EncodingProvider;
import net.aholbrook.paseto.exception.InvalidFooterException;
import net.aholbrook.paseto.exception.InvalidHeaderException;
import net.aholbrook.paseto.util.StringUtils;

import java.nio.charset.Charset;
import java.util.regex.Pattern;

public abstract class Paseto {
	final static String SEPARATOR = ".";
	final static String PURPOSE_LOCAL = "local";
	final static String PURPOSE_PUBLIC = "public";

	final Base64Provider base64Provider;
	final EncodingProvider encodingProvider;
	final NonceGenerator nonceGenerator;

	public Paseto(Base64Provider base64Provider, EncodingProvider encodingProvider, NonceGenerator nonceGenerator) {
		this.base64Provider = base64Provider;
		this.encodingProvider = encodingProvider;
		this.nonceGenerator = nonceGenerator;
	}

	public abstract String encrypt(Object payload, byte[] key, String footer);

	public abstract <_Payload> _Payload decrypt(String token, byte[] key, String footer, Class<_Payload> payloadClass);

	public abstract String sign(Object payload, byte[] key, String footer);

	public abstract <_Payload> _Payload verify(String token, byte[] pk, String footer, Class<_Payload> payloadClass);

	public abstract KeyPair generateKeyPair();

	public String encrypt(Object payload, byte[] key) {
		return encrypt(payload, key, null);
	}

	public String encrypt(Object payload, byte[] key, Object footer) {
		return encrypt(payload, key, encodingProvider.encode(footer));
	}

	public <_Payload> _Payload decrypt(String token, byte[] key, Class<_Payload> payloadClass) {
		return decrypt(token, key, null, payloadClass);
	}

	public <_Payload> _Payload decrypt(String token, byte[] key, Object footer, Class<_Payload> payloadClass) {
		return decrypt(token, key, encodingProvider.encode(footer), payloadClass);
	}

	public String sign(Object payload, byte[] sk) {
		return sign(payload, sk, null);
	}

	public String sign(Object payload, byte[] sk, Object footer) {
		return sign(payload, sk, encodingProvider.encode(footer));
	}

	public <_Payload> _Payload verify(String token, byte[] pk, Class<_Payload> payloadClass) {
		return verify(token, pk, null, payloadClass);
	}

	public <_Payload> _Payload verify(String token, byte[] pk, Object footer, Class<_Payload> payloadClass) {
		return verify(token, pk, encodingProvider.encode(footer), payloadClass);
	}

	public <_Payload> TokenWithFooter<_Payload, String> decryptWithFooter(String token, byte[] key,
			Class<_Payload> payloadClass) {
		_Payload payload = decrypt(token, key, payloadClass);
		String footer = extractFooter(token);
		return new TokenWithFooter<>(payload, footer);
	}

	public <_Payload, _Footer> TokenWithFooter<_Payload, _Footer> decryptWithFooter(String token, byte[] key,
			Class<_Payload> payloadClass, Class<_Footer> footerClass) {
		_Payload payload = decrypt(token, key, payloadClass);
		_Footer footer = extractFooter(token, footerClass);
		return new TokenWithFooter<>(payload, footer);
	}

	public <_Payload> TokenWithFooter<_Payload, String> verifyWithFooter(String token, byte[] pk,
			Class<_Payload> payloadClass) {
		_Payload payload = verify(token, pk, payloadClass);
		String footer = extractFooter(token);
		return new TokenWithFooter<>(payload, footer);
	}

	public <_Payload, _Footer> TokenWithFooter<_Payload, _Footer> verifyWithFooter(String token, byte[] pk,
			Class<_Payload> payloadClass, Class<_Footer> footerClass) {
		_Payload payload = verify(token, pk, payloadClass);
		_Footer footer = extractFooter(token, footerClass);
		return new TokenWithFooter<>(payload, footer);
	}

	public String extractFooter(String token) {
		String footer = split(token)[3];
		if (!StringUtils.isEmpty(footer)) {
			return StringUtils.fromUtf8Bytes(base64Provider.decodeFromString(footer));
		}

		return null;
	}

	public <_Footer> _Footer extractFooter(String token, Class<_Footer> footerClass) {
		String footer = extractFooter(token);
		if (!StringUtils.isEmpty(footer)) {
			return encodingProvider.decode(footer, footerClass);
		}

		return null;
	}

	/**
	 * Splits a Paseto token into its 4 sections: VERSION, PURPOSE, PAYLOAD, FOOTER.
	 *
	 * If the token does not contain a footer, then the 4th string in the array will be null. If the string does
	 * not contain either 3 or 4 sections separated by a period (ASCII 2E) then a null array will be returned as the
	 * token cannot be valid.
	 *
	 * @param token Paseto token.
	 * @return Array of 4 strings, each containing 1 paseto token section. null if string cannot be a Paseto token.
	 */
	String[] split(String token) {
		if (!StringUtils.isEmpty(token)) {
			String[] tokens = token.split(Pattern.quote(SEPARATOR));

			if (tokens.length == 4) {
				return tokens;
			} else if (tokens.length == 3) {
				return new String[] {tokens[0], tokens[1], tokens[2], null};
			}
		}

		return null;
	}

	void checkHeader(String token, String[] sections, String expectedHeader) {
		if (!token.startsWith(expectedHeader)) {
			throw new InvalidHeaderException(sections[0] + SEPARATOR + sections[1] + SEPARATOR, expectedHeader, token);
		}
	}

	String decodeFooter(String token, String[] sections, String expectedFooter) {
		String userFooter = StringUtils.ntes(sections[3]);
		String decodedFooter = new String(base64Provider.decodeFromString(userFooter), Charset.forName("UTF-8"));

		// Check the footer if expected footer is not empty, otherwise we just return the footer without checking. This
		// is find though, as the footer is covered by the token PAE signature. This check exists for proper error
		// reporting, and is not a requirement for security.
		if (!StringUtils.isEmpty(expectedFooter) && !StringUtils.isEqual(decodedFooter, expectedFooter)) {
			throw new InvalidFooterException(decodedFooter, expectedFooter, token);
		}

		return decodedFooter;
	}

	<_Payload> _Payload decode(byte[] payload, Class<_Payload> payloadClass) {
		return encodingProvider.decode(new String(payload, Charset.forName("UTF-8")), payloadClass);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy