org.picketlink.json.jose.AbstractJWSBuilder Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.picketlink.json.jose;
import org.picketlink.json.JsonConstants;
import org.picketlink.json.JsonException;
import org.picketlink.json.jose.crypto.Algorithm;
import org.picketlink.json.jwt.JWTBuilder;
import javax.json.JsonObject;
import javax.json.JsonValue;
import java.lang.reflect.Constructor;
import static javax.json.JsonValue.ValueType.ARRAY;
import static org.picketlink.json.JsonConstants.COMMON.ALG;
import static org.picketlink.json.JsonConstants.COMMON.HEADER_JSON_WEB_KEY;
import static org.picketlink.json.JsonConstants.COMMON.KEY_ID;
import static org.picketlink.json.JsonConstants.COMMON.PERIOD;
import static org.picketlink.json.JsonMessages.MESSAGES;
import static org.picketlink.json.jose.crypto.Algorithm.HS256;
import static org.picketlink.json.jose.crypto.Algorithm.HS384;
import static org.picketlink.json.jose.crypto.Algorithm.HS512;
import static org.picketlink.json.jose.crypto.Algorithm.RS256;
import static org.picketlink.json.jose.crypto.Algorithm.RS384;
import static org.picketlink.json.jose.crypto.Algorithm.RS512;
import static org.picketlink.json.util.Base64Util.b64Decode;
/**
* The Class AbstractJWSBuilder for building JSON Web Signature.
*
* @author Pedro Igor
* @param the generic type
* @param the generic type
*/
public abstract class AbstractJWSBuilder> extends JWTBuilder {
private byte[] key;
/**
* Instantiates a new abstract JWS builder.
*
* @param tokenType the token type
*/
protected AbstractJWSBuilder(Class tokenType) {
super(tokenType);
header(ALG, Algorithm.NONE.getAlgorithm());
}
/**
* The kid (key ID) member can be used to match a specific key. This can be used, for instance, to choose among a set of
* keys within a JWK Set during key rollover. The structure of the kid value is unspecified. When kid values are used within
* a JWK Set, different keys within the JWK Set SHOULD use distinct kid values. (One example in which different keys might
* use the same kid value is if they have different kty (key type) values but are considered to be equivalent alternatives
* by the application using them.) The kid value is a case-sensitive string. Use of this member is OPTIONAL.
*
*
* When used with JWS or JWE, the kid value is used to match a JWS or JWE kid Header Parameter value.
*
* @param kid the kid
* @return the b
*/
public B kid(String kid) {
header(KEY_ID, kid);
return (B) this;
}
/**
* Sets HMAC using SHA-256 as signature algorithm.
*
* @param key the key
* @return the b
*/
public B hmac256(byte[] key) {
header(ALG, HS256.name());
this.key = key;
return (B) this;
}
/**
* Sets HMAC using SHA-384 as signature algorithm.
*
* @param key the key
* @return the b
*/
public B hmac384(byte[] key) {
header(ALG, HS384.name());
this.key = key;
return (B) this;
}
/**
* Sets HMAC using SHA-512 as signature algorithm.
*
* @param key the key
* @return the b
*/
public B hmac512(byte[] key) {
header(ALG, HS512.name());
this.key = key;
return (B) this;
}
/**
* Sets RSASSA-PKCS-v1_5 using SHA-256 as signature algorithm.
*
* @param key the key
* @return the b
*/
public B rsa256(byte[] key) {
header(ALG, RS256.name());
this.key = key;
return (B) this;
}
/**
* Sets RSASSA-PKCS-v1_5 using SHA-384 as signature algorithm.
*
* @param key the key
* @return the b
*/
public B rsa384(byte[] key) {
header(ALG, RS384.name());
this.key = key;
return (B) this;
}
/**
* Sets RSASSA-PKCS-v1_5 using SHA-512 as signature algorithm.
*
* @param key the key
* @return the b
*/
public B rsa512(byte[] key) {
header(ALG, RS512.name());
this.key = key;
return (B) this;
}
/**
* Sets JWKSet key(s) as a JSON Web Key Set for JWS Header.
*
* @param keySet the key set
* @return the b
*/
public B keys(JWKSet keySet) {
header(HEADER_JSON_WEB_KEY, keySet.getJsonObject().getJsonArray(HEADER_JSON_WEB_KEY));
return (B) this;
}
/**
* Adds key(s) into the JWKSet.
*
* @param keys the keys
* @return the b
*/
public B keys(JWK... keys) {
JWKSet jwkSet = new JWKSet(keys);
return keys(jwkSet);
}
/**
* @see org.picketlink.json.jwt.JWTBuilder#build(javax.json.JsonObject, javax.json.JsonObject)
*/
@Override
protected T build(JsonObject headersObject, JsonObject claimsObject) {
try {
Constructor constructor = getTokenType().getDeclaredConstructor(JsonObject.class, JsonObject.class, byte[].class);
constructor.setAccessible(true);
return (T) constructor.newInstance(headersObject, claimsObject, this.key);
} catch (Exception nsme) {
throw MESSAGES.couldNotCreateToken(getTokenType(), nsme);
}
}
/**
*
* Builds a {@link org.picketlink.json.jose.JWS} instance from the given json
string representing an encoded
* JWS.
*
*
*
* This method first tries to check if the given JWS provides any JWK representing the public key that should be used to
* validate the signature.
*
*
*
* If no JWK is found, this method does not validate the signature but returns a JWS instance.
*
*
* @param json The encoded JSON string representing a JWS.
* @return A JWS representing the given encoded JSON string.
* @throws JsonException the json exception
*/
@Override
public T build(String json) throws JsonException {
T uncheckedToken = super.build(json);
String keyId = uncheckedToken.getHeader(KEY_ID);
if (keyId != null) {
JsonValue keysHeader = uncheckedToken.getHeaders().get(HEADER_JSON_WEB_KEY);
if (keysHeader != null) {
if (ARRAY.equals(keysHeader.getValueType())) {
JWKSet jwkSet = new JWKSet((javax.json.JsonArray) keysHeader);
JWK jwk = jwkSet.get(keyId);
if (!JsonConstants.RSA.equals(jwk.getKeyType())) {
throw MESSAGES.cryptoUnsupportedKey(jwk.getKeyType());
}
try {
this.key = jwk.toRSAPublicKey().getEncoded();
} catch (Exception e) {
throw MESSAGES.cryptoCouldNotParseKey(jwk.toString(), e);
}
}
}
}
if (this.key == null) {
return uncheckedToken;
}
return build(json, this.key);
}
/**
*
* Builds a {@link JWS} and validates its signatures based on the given encoded public key
.
*
*
* @param json The encoded JSON string representing a JWS.
* @param key The encoded representation of a public key.
* @return A JWS representing the given encoded JSON string.
* @throws JsonException the json exception
*/
public T build(String json, byte[] key) throws JsonException {
if (key == null) {
throw MESSAGES.invalidNullArgument("Encoded Public Key.");
}
T token = super.build(json);
Algorithm algorithm = Algorithm.resolve(token.getAlgorithm().toUpperCase());
if (!algorithm.isNone()) {
String[] portions = json.split("\\" + PERIOD);
if (portions.length < 2) {
throw MESSAGES.cryptoSignatureNotPresent(json);
}
byte[] payload = json.substring(0, json.lastIndexOf(PERIOD)).getBytes();
byte[] signature = b64Decode(portions[2]);
boolean validSignature = algorithm.getSignatureProvider().verify(payload, algorithm, signature, key);
if (!validSignature) {
throw MESSAGES.cryptoInvalidSignature(json);
}
}
return token;
}
}