JwtHeaders.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils.security;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.cxf.common.util.Base64UrlUtility;
import org.apache.cxf.rs.security.jose.common.KeyManagementUtils;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
import org.apache.cxf.rt.security.crypto.MessageDigestUtils;
import org.openspcoop2.utils.UtilsException;
/**
* JwtHeaders
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class JwtHeaders {
public static final String JWT_HDR_ALG = "alg"; // (Algorithm) Header Parameter
public static final String JWT_HDR_JKU = "jku"; // (JWK Set URL) Header Parameter
public static final String JWT_HDR_JWK = "jwk"; // (JSON Web Key) Header Parameter
public static final String JWT_HDR_KID = "kid"; // (Key ID) Header Parameter
public static final String JWT_HDR_X5U = "x5u"; // (X.509 URL) Header Parameter
public static final String JWT_HDR_X5C = "x5c"; // (X.509 Certificate Chain) Header Parameter
public static final String JWT_HDR_X5T = "x5t"; // (X.509 Certificate SHA-1 Thumbprint) Header Parameter
public static final String JWT_HDR_X5t_S256 = "x5t#S256"; // (X.509 Certificate SHA-256 Thumbprint) Header Parameter
public static final String JWT_HDR_TYP = "typ"; // (Type) Header Parameter
public static final String JWT_HDR_CTY = "cty"; // (Content Type) Header Parameter
public static final String JWT_HDR_CRIT = "crit"; // (Critical) Header Parameter
public static final String JWT_HDR_ENC = "enc"; // (Encryption Algorithm) Header Parameter [solo in jwe]
public static final String JWT_HDR_ZIP = "zip"; // (Compression Algorithm) Header Parameter [solo in jwe]
private String type;
private String contentType;
private String kid;
private List<String> criticalHeaders = new ArrayList<>();
private URI x509Url;
private List<X509Certificate> x509c = new ArrayList<>(); // i certificati servono anche per sha1 e sha256, il field addX5C serve quindi per capire se poi far uscire anche X5C
private boolean addX5C = false;
boolean x509IncludeCertSha1 = false;
boolean x509IncludeCertSha256 = false;
private URI jwkUrl;
private JsonWebKey jwKey;
private HashMap<String, String> extensions = new HashMap<>();
public void setType(String type) {
this.type = type;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getKid() {
return this.kid;
}
public void addCriticalHeader(String hdr) {
this.criticalHeaders.add(hdr);
}
public void setX509Url(URI x509Url) {
this.x509Url = x509Url;
}
public void addX509cert(X509Certificate x509c) {
this.x509c.add(x509c);
}
public void setAddX5C(boolean addX5C) {
this.addX5C = addX5C;
}
public boolean isX509IncludeCertSha1() {
return this.x509IncludeCertSha1;
}
public boolean isX509IncludeCertSha256() {
return this.x509IncludeCertSha256;
}
public void setJwkUrl(URI jwkUrl) {
this.jwkUrl = jwkUrl;
}
public void setJwKey(JsonWebKey jwKey) {
this.jwKey = jwKey;
}
public void setJwKey(JsonWebKeys jsonWebKeys, String alias) throws UtilsException {
this.jwKey = JsonUtils.readKey(jsonWebKeys, alias);
}
public void addExtension(String hdr, String value) {
this.extensions.put(hdr, value);
}
public String getType() {
return this.type;
}
public String getContentType() {
return this.contentType;
}
public void setKid(String kid) {
this.kid = kid;
}
public List<String> getCriticalHeaders() {
return this.criticalHeaders;
}
public URI getX509Url() {
return this.x509Url;
}
public boolean isAddX5C() {
return this.addX5C;
}
public List<X509Certificate> getX509c() {
return this.x509c;
}
public void setX509IncludeCertSha1(boolean includeCertSha1) {
this.x509IncludeCertSha1 = includeCertSha1;
}
public void setX509IncludeCertSha256(boolean includeCertSha256) {
this.x509IncludeCertSha256 = includeCertSha256;
}
public URI getJwkUrl() {
return this.jwkUrl;
}
public JsonWebKey getJwKey() {
return this.jwKey;
}
public HashMap<String, String> getExtensions() {
return this.extensions;
}
public List<String> headers(){
List<String> list = new ArrayList<>();
if(this.type!=null) {
list.add(JWT_HDR_TYP);
}
if(this.contentType!=null) {
list.add(JWT_HDR_CTY);
}
if(this.kid!=null) {
list.add(JWT_HDR_KID);
}
if(this.criticalHeaders!=null && !this.criticalHeaders.isEmpty()) {
list.add(JWT_HDR_CRIT);
}
if(this.x509Url!=null) {
list.add(JWT_HDR_X5U);
}
if(this.x509c!=null && !this.x509c.isEmpty()) {
// fix: lo aggiungo solo se non c'è la url. Nell'oggetto JwtHreader il certificato ho dovuto mettercelo per creare i sha
//if(!list.contains(JWT_HDR_X5U)) {
// il fix era errato, aggiunto field apposito 'addX5C'
if(this.addX5C) {
list.add(JWT_HDR_X5C);
}
}
if(this.x509IncludeCertSha1 && this.x509c!=null && !this.x509c.isEmpty()) {
list.add(JWT_HDR_X5T);
}
if(this.x509IncludeCertSha256) {
list.add(JWT_HDR_X5t_S256);
}
if(this.jwkUrl!=null) {
list.add(JWT_HDR_JKU);
}
if(this.jwKey!=null) {
list.add(JWT_HDR_JWK);
}
if(this.extensions!=null && !this.extensions.isEmpty()) {
Iterator<String> hdrIt = this.extensions.keySet().iterator();
while (hdrIt.hasNext()) {
String hdr = (String) hdrIt.next();
list.add(hdr);
}
}
return list;
}
public void fillJwsHeaders(org.apache.cxf.rs.security.jose.common.JoseHeaders hdrs, boolean forceOverride, String algorithm) throws Exception {
if(this.type!=null) {
if(!hdrs.containsHeader(JWT_HDR_TYP) || forceOverride) {
hdrs.setHeader(JWT_HDR_TYP, this.type);
}
}
if(this.contentType!=null) {
if(!hdrs.containsHeader(JWT_HDR_CTY) || forceOverride) {
hdrs.setContentType(this.contentType);
}
}
if(this.kid!=null) {
if(!hdrs.containsHeader(JWT_HDR_KID) || forceOverride) {
hdrs.setKeyId(this.kid);
}
}
if(this.criticalHeaders!=null && !this.criticalHeaders.isEmpty()) {
List<String> headers = new ArrayList<>();
if(hdrs.containsHeader(JWT_HDR_CRIT)) {
headers = hdrs.getCritical();
if(headers==null) {
headers = new ArrayList<>();
}
}
for (String ch : this.criticalHeaders) {
if(headers.contains(ch)==false) {
headers.add(ch);
}
}
/*
StringBuilder bf = new StringBuilder();
for (String ch : headers) {
if(bf.length()>0) {
bf.append(",");
}
bf.append("\"").append(ch).append("\"");
}
hdrs.setHeader(JWT_HDR_CRIT, "["+bf.toString()+"]");*/
hdrs.setCritical(headers);
}
if(this.x509Url!=null) {
if(!hdrs.containsHeader(JWT_HDR_X5U) || forceOverride) {
hdrs.setX509Url(this.x509Url.toString());
}
}
if(this.x509c!=null && !this.x509c.isEmpty()) {
if(!hdrs.containsHeader(JWT_HDR_X5C) || forceOverride) {
// fix: lo aggiungo solo se non c'è la url. Nell'oggetto JwtHreader il certificato ho dovuto mettercelo per creare i sha
//if(!hdrs.containsHeader(JWT_HDR_X5U)) {
// il fix era errato, aggiunto field apposito 'addX5C'
if(this.addX5C) {
X509Certificate[] chain = this.x509c.toArray(new X509Certificate[1]);
hdrs.setX509Chain(KeyManagementUtils.encodeX509CertificateChain(chain));
}
}
}
if(this.x509IncludeCertSha1 && this.x509c!=null && !this.x509c.isEmpty()) {
if(!hdrs.containsHeader(JWT_HDR_X5T) || forceOverride) {
X509Certificate[] chain = this.x509c.toArray(new X509Certificate[1]);
byte[] digestB = MessageDigestUtils.createDigest(chain[0].getEncoded(), MessageDigestUtils.ALGO_SHA_1);
String digest = Base64UrlUtility.encode(digestB);
hdrs.setX509Thumbprint(digest);
}
}
if(this.x509IncludeCertSha256) {
if(!hdrs.containsHeader(JWT_HDR_X5t_S256) || forceOverride) {
X509Certificate[] chain = this.x509c.toArray(new X509Certificate[1]);
byte[] digestB = MessageDigestUtils.createDigest(chain[0].getEncoded(), MessageDigestUtils.ALGO_SHA_256);
String digest = Base64UrlUtility.encode(digestB);
hdrs.setX509ThumbprintSHA256(digest);
}
}
if(this.jwkUrl!=null) {
if(!hdrs.containsHeader(JWT_HDR_JKU) || forceOverride) {
hdrs.setJsonWebKeysUrl(this.jwkUrl.toString());
}
}
if(this.jwKey!=null) {
if(!hdrs.containsHeader(JWT_HDR_JWK) || forceOverride) {
JwkUtils.includeCertChain(this.jwKey, hdrs, algorithm);
JwkUtils.includePublicKey(this.jwKey, hdrs, algorithm);
}
}
if(this.extensions!=null && !this.extensions.isEmpty()) {
Iterator<String> hdrIt = this.extensions.keySet().iterator();
while (hdrIt.hasNext()) {
String hdr = (String) hdrIt.next();
if(!hdrs.containsHeader(hdr) || forceOverride) {
String value = this.extensions.get(hdr);
hdrs.setHeader(hdr,value);
}
}
}
}
}