JWK.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.certificate;

import java.security.PrivateKey;
import java.security.PublicKey;

import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
import org.apache.cxf.rs.security.jose.jwk.JwkReaderWriter;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.UtilsRuntimeException;
import org.openspcoop2.utils.json.JSONUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.RSAKey;

/**	
 * JWK
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class JWK {

	private JwkReaderWriter engineCxf = new JwkReaderWriter();
	private String jwkJson;
	private String jwkJsonPretty;
	private JsonWebKey jwkCxf;
	private com.nimbusds.jose.jwk.JWK jwkNimbusds;
	private JsonNode jwkNode;
	
	public JWK(String json) {
		this.jwkJson = json;
	}
	
	public JWK(JsonWebKey jwk) {
		this.jwkCxf = jwk;
	}
	
	public JWK(com.nimbusds.jose.jwk.JWK jwk) {
		this.jwkNimbusds = jwk;
	}
	
	public JWK(KeyStore keystore, String alias) throws UtilsException {
		this(keystore, alias, null, null, true);
	}
	public JWK(KeyStore keystore, String alias, KeyUse use) throws UtilsException {
		this(keystore, alias, null, use, true);
	}
	public JWK(KeyStore keystore, String alias, boolean kid) throws UtilsException {
		this(keystore, alias, null, null, kid);
	}
	public JWK(KeyStore keystore, String alias, KeyUse use, boolean kid) throws UtilsException {
		this(keystore, alias, null, use, kid);
	}
	public JWK(KeyStore keystore, String alias, String passwordPrivateKey) throws UtilsException {
		this(keystore, alias, passwordPrivateKey, null, true);
	}
	public JWK(KeyStore keystore, String alias, String passwordPrivateKey, KeyUse use) throws UtilsException {
		this(keystore, alias, passwordPrivateKey, use, true);
	}
	public JWK(KeyStore keystore, String alias, String passwordPrivateKey, boolean kid) throws UtilsException {
		this(keystore, alias, passwordPrivateKey, null, kid);
	}
	public JWK(KeyStore keystore, String alias, String passwordPrivateKey, KeyUse use, boolean kid) throws UtilsException {
		try {
			if(!keystore.existsAlias(alias)) {
				throw new UtilsException("Alias '"+alias+"' undefined");
			}
			PublicKey publicKey = keystore.getPublicKey(alias);
			if(publicKey instanceof java.security.interfaces.RSAPublicKey) {
				PrivateKey privateKey = null;
				if(passwordPrivateKey!=null) {
					privateKey = keystore.getPrivateKey(alias, passwordPrivateKey);
				}
				String aliasP = alias;
				if(!kid) {
					aliasP = null;
				}
				initEngine(publicKey, privateKey, aliasP, use);
			}
			else {
				throw new UtilsException("Unsupported type '"+publicKey.getClass().getName()+"'");
			}
		}catch(Exception e) {
			throw new UtilsException(e.getMessage(),e);
		}
	}
	
	public JWK(PublicKey publicKey) throws UtilsException {
		this(publicKey, null, null, null);
	}
	public JWK(PublicKey publicKey, String kid) throws UtilsException {
		this(publicKey, null, kid, null);
	}
	public JWK(PublicKey publicKey, KeyUse use) throws UtilsException {
		this(publicKey, null, null, use);
	}
	public JWK(PublicKey publicKey, String kid, KeyUse use) throws UtilsException {
		this(publicKey, null, kid, use);
	}
	public JWK(PublicKey publicKey, PrivateKey privateKey) throws UtilsException {
		this(publicKey, privateKey, null, null);
	}
	public JWK(PublicKey publicKey, PrivateKey privateKey, String kid) throws UtilsException {
		this(publicKey, privateKey, kid, null);
	}
	public JWK(PublicKey publicKey, PrivateKey privateKey, KeyUse use) throws UtilsException {
		this(publicKey, privateKey, null, use);
	}
	public JWK(PublicKey publicKey, PrivateKey privateKey, String kid, KeyUse use) throws UtilsException {
		initEngine(publicKey, privateKey, kid, use);
	}
	private void initEngine(PublicKey publicKey, PrivateKey privateKey, String kid, KeyUse use) throws UtilsException {
		try {
			if(publicKey instanceof java.security.interfaces.RSAPublicKey) {
				java.security.interfaces.RSAPublicKey p = (java.security.interfaces.RSAPublicKey) publicKey;
				RSAKey.Builder builder = new RSAKey.Builder(p);
				if(privateKey!=null) {
					builder.privateKey(privateKey);
				}
				if(kid!=null) {
					builder.keyID(kid);
				}
				if(use!=null) {
					builder.keyUse(use);
				}
				this.jwkNimbusds = builder.build();
			}
			else {
				if(publicKey==null) {
					throw new UtilsException("PublicKey undefined");
				}
				else {
					throw new UtilsException("Unsupported type '"+publicKey.getClass().getName()+"'");
				}
			}
		}catch(Exception e) {
			throw new UtilsException(e.getMessage(),e);
		}
	}
	
	public JWK(javax.crypto.SecretKey secretKey) throws UtilsException {
		this(secretKey, null, null, null);
	}
	public JWK(javax.crypto.SecretKey secretKey, String kid) throws UtilsException {
		this(secretKey, kid, null, null);
	}
	public JWK(javax.crypto.SecretKey secretKey, String kid, String algorithm) throws UtilsException {
		this(secretKey, kid, null, algorithm);
	}
	public JWK(javax.crypto.SecretKey secretKey, KeyUse use) throws UtilsException {
		this(secretKey, null, use, null);
	}
	public JWK(javax.crypto.SecretKey secretKey, KeyUse use, String algorithm) throws UtilsException {
		this(secretKey, null, use, algorithm);
	}
	public JWK(javax.crypto.SecretKey secretKey, String kid, KeyUse use) throws UtilsException {
		this(secretKey, kid, use, null);
	}
	public JWK(javax.crypto.SecretKey secretKey, String kid, KeyUse use, String algorithm) throws UtilsException {
		try {
			OctetSequenceKey.Builder builder = new OctetSequenceKey.Builder(secretKey);
			if(algorithm!=null) {
				builder = builder.algorithm(Algorithm.parse(algorithm));
			}
			if(kid!=null) {
				builder = builder.keyID(kid);
			}
			if(use!=null) {
				builder = builder.keyUse(use);
			}
			this.jwkNimbusds = builder.build();
		}catch(Exception e) {
			throw new UtilsException(e.getMessage(),e);
		}
	}
	
	private synchronized void initCxf() throws UtilsException {
		if(this.jwkCxf==null) {
			if(this.jwkJson==null){
				throw new UtilsException("Json not defined");
			}
			this.jwkCxf = this.engineCxf.jsonToJwk(this.jwkJson);
		}
	}
	public JsonWebKey getJsonWebKey() throws UtilsException {
		if(this.jwkCxf==null) {
			this.initCxf();
		}
		return this.jwkCxf;
	}
	
	private synchronized void initNimbusds() throws UtilsException {
		if(this.jwkNimbusds==null) {
			if(this.jwkJson==null){
				throw new UtilsException("Json not defined");
			}
			try {
				this.jwkNimbusds = RSAKey.parse(this.jwkJson);
			}catch(Exception e) {
				throw new UtilsException(e.getMessage(),e);
			}
		}
	}
	public com.nimbusds.jose.jwk.JWK getJWK() throws UtilsException {
		if(this.jwkNimbusds==null) {
			this.initNimbusds();
		}
		return this.jwkNimbusds;
	}
	
	private synchronized void initJson() throws UtilsException {
		if(this.jwkJson==null) {
			if(this.jwkCxf==null && this.jwkNimbusds==null){
				throw new UtilsException("JWK not defined");
			}
			if(this.jwkCxf!=null) {
				try {
					this.jwkJson = this.engineCxf.jwkToJson(this.jwkCxf);
				}catch(Exception e) {
					throw new UtilsException(e.getMessage(),e);
				}
			}
			else {
				try {
					this.jwkJson = this.jwkNimbusds.toJSONString();
				}catch(Exception e) {
					throw new UtilsException(e.getMessage(),e);
				}
			}
		}
	}
	public String getJson() throws UtilsException {
		if(this.jwkJson==null) {
			this.initJson();
		}
		return this.jwkJson;
	}
	
	private synchronized void initJsonPretty() throws UtilsException {
		if(this.jwkJsonPretty==null) {
			try {
				if(this.jwkNode==null) {
					initNode();
				}
				this.jwkJsonPretty = JSONUtils.getInstance(true).toString(this.jwkNode);
			}catch(Exception e) {
				throw new UtilsException(e.getMessage(),e);
			}
		}
	}
	public String getJsonPretty() throws UtilsException {
		if(this.jwkJsonPretty==null) {
			this.initJsonPretty();
		}
		return this.jwkJsonPretty;
	}
	
	private synchronized void initNode() throws UtilsException {
		if(this.jwkNode==null) {
			try {
				if(this.jwkJson==null) {
					initJson();
				}
				this.jwkNode = JSONUtils.getInstance().getAsNode(this.jwkJson);
			}catch(Exception e) {
				throw new UtilsException(e.getMessage(),e);
			}
		}
	}
	public JsonNode getNode() throws UtilsException {
		if(this.jwkNode==null) {
			this.initNode();
		}
		return this.jwkNode;
	}
	
	@Override
	public String toString() {
		try {
			return this.getJsonPretty();
		}catch(Exception e) {
			throw new UtilsRuntimeException(e.getMessage(),e);
		}
	}
}