JsonSignature.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */


  20. package org.openspcoop2.utils.security;

  21. import java.security.PrivateKey;
  22. import java.util.Iterator;
  23. import java.util.Properties;

  24. import javax.crypto.SecretKey;

  25. import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
  26. import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
  27. import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
  28. import org.apache.cxf.rs.security.jose.jws.JwsCompactProducer;
  29. import org.apache.cxf.rs.security.jose.jws.JwsException;
  30. import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
  31. import org.apache.cxf.rs.security.jose.jws.JwsJsonProducer;
  32. import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
  33. import org.apache.cxf.rs.security.jose.jws.JwsUtils;
  34. import org.openspcoop2.utils.UtilsException;
  35. import org.openspcoop2.utils.certificate.KeyStore;
  36. import org.openspcoop2.utils.certificate.KeystoreType;

  37. /**
  38.  * JsonSignature
  39.  *
  40.  * @author Poli Andrea (apoli@link.it)
  41.  * @author $Author$
  42.  * @version $Rev$, $Date$
  43.  */
  44. public class JsonSignature {

  45.     private JwsSignatureProvider provider;
  46.     private JWSOptions options;
  47.     private JwsHeaders headers;
  48.     private JwtHeaders jwtHeaders;
  49.    
  50.     public JsonSignature(Properties props, JWSOptions options) throws UtilsException{
  51.         this(props, null, options);
  52.     }
  53.     public JsonSignature(Properties props, JwtHeaders jwtHeaders, JWSOptions options) throws UtilsException{
  54.         try {
  55.             this.headers = new JwsHeaders(props);
  56.             this.provider = JsonUtils.getJwsSymmetricProvider(props);
  57.             if(this.provider==null) {
  58.                 try {
  59.                     this.provider = JwsUtils.loadSignatureProvider(JsonUtils.newMessage(), props, this.headers);
  60.                 }catch(JwsException jwsExc) {
  61.                     if(jwsExc.getMessage()!=null && jwsExc.getMessage().contains("NO_PROVIDER")) {
  62.                         // caso in cui la chiave privata PKCS11 non e' stata mappata in un PrivateKeyJwsSignatureProvider
  63.                         // caso pkcs11 dove la chiave privata non e' ne ECPrivateKey ne RSAPrivateKey gestita dal metodo getPrivateKeySignatureProvider
  64.                         // ad es. può essere sun.security.pkcs11.P11Key$P11PrivateKey
  65.                         this.provider = JsonUtils.getJwsAsymmetricProvider(props);
  66.                         if(this.provider==null) {
  67.                             // rilancio eccezione precedente
  68.                             throw jwsExc;
  69.                         }
  70.                     }
  71.                 }
  72.             }
  73.             this.options=options;
  74.             this.jwtHeaders = jwtHeaders;
  75.         }catch(Exception t) {
  76.             throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.SENDER,t);
  77.         }
  78.     }

  79.     public JsonSignature(java.security.KeyStore keystore, boolean secretKey, String alias, String passwordPrivateKey, String signatureAlgorithm, JWSOptions options) throws UtilsException{
  80.         this(new KeyStore(keystore), secretKey, alias, passwordPrivateKey, signatureAlgorithm,
  81.                 null, options);
  82.     }
  83.     public JsonSignature(KeyStore keystore, boolean secretKey, String alias, String passwordPrivateKey, String signatureAlgorithm, JWSOptions options) throws UtilsException{
  84.         this(keystore, secretKey, alias, passwordPrivateKey, signatureAlgorithm,
  85.                 null, options);
  86.     }
  87.     public JsonSignature(java.security.KeyStore keystore, boolean secretKey, String alias, String passwordPrivateKey, String signatureAlgorithm,
  88.             JwtHeaders jwtHeaders, JWSOptions options) throws UtilsException{
  89.         this(new KeyStore(keystore),secretKey, alias, passwordPrivateKey, signatureAlgorithm, jwtHeaders, options);
  90.     }
  91.     public JsonSignature(KeyStore keystore, boolean secretKey, String alias, String passwordPrivateKey, String signatureAlgorithm,
  92.             JwtHeaders jwtHeaders, JWSOptions options) throws UtilsException{
  93.         try {
  94.            
  95.             org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
  96.             if(secretKey) {
  97.                 String tipo = KeystoreType.JCEKS.getLabel();
  98.                 SecretKey secret = keystore.getSecretKey(alias, passwordPrivateKey);
  99.                 if(KeystoreType.PKCS11.getNome().equalsIgnoreCase(keystore.getKeystoreType())) {
  100.                     tipo = KeystoreType.PKCS11.getLabel();
  101.                     SecretKeyPkcs11 secretKeyPkcs11 = new SecretKeyPkcs11(keystore.getKeystoreProvider(), secret);
  102.                     this.provider = new HmacJwsSignatureProviderExtended(secretKeyPkcs11, algo);
  103.                 }
  104.                 else {
  105.                     this.provider = JwsUtils.getHmacSignatureProvider(secret.getEncoded(), algo);
  106.                 }
  107.                 if(this.provider==null) {
  108.                     throw new UtilsException("("+tipo+") JwsSignatureProvider init failed; check signature algorithm ("+signatureAlgorithm+")");
  109.                 }
  110.             }
  111.             else {
  112.                 PrivateKey pKey = keystore.getPrivateKey(alias, passwordPrivateKey);
  113.                 this.provider = JwsUtils.getPrivateKeySignatureProvider(pKey, algo);
  114.                 if(this.provider==null) {
  115.                     // caso in cui la chiave privata PKCS11 non e' stata mappata in un PrivateKeyJwsSignatureProvider
  116.                     // caso pkcs11 dove la chiave privata non e' ne ECPrivateKey ne RSAPrivateKey gestita dal metodo getPrivateKeySignatureProvider
  117.                     // ad es. può essere sun.security.pkcs11.P11Key$P11PrivateKey
  118.                     this.provider = JsonUtils.getJwsAsymmetricProvider(pKey, algo);
  119.                     if(this.provider==null) {
  120.                         throw new UtilsException("("+keystore.getKeystore().getType()+") JwsSignatureProvider init failed; check signature algorithm ("+signatureAlgorithm+")");
  121.                     }
  122.                 }
  123.             }
  124.             this.options=options;
  125.             this.jwtHeaders = jwtHeaders;
  126.         }catch(Exception t) {
  127.             throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.SENDER,t);
  128.         }
  129.     }
  130.    
  131.     public JsonSignature(JsonWebKeys jsonWebKeys, boolean secretKey, String alias, String signatureAlgorithm, JWSOptions options) throws UtilsException{
  132.         this(jsonWebKeys, secretKey, alias, signatureAlgorithm,
  133.                 null, options);
  134.     }
  135.     public JsonSignature(JsonWebKeys jsonWebKeys, boolean secretKey, String alias, String signatureAlgorithm,
  136.             JwtHeaders jwtHeaders, JWSOptions options) throws UtilsException{
  137.         this(JsonUtils.readKey(jsonWebKeys, alias),secretKey,signatureAlgorithm,jwtHeaders,options);
  138.     }
  139.    
  140.     public JsonSignature(JsonWebKey jsonWebKey, boolean secretKey, String signatureAlgorithm, JWSOptions options) throws UtilsException{
  141.         this(jsonWebKey, secretKey, signatureAlgorithm,
  142.                 null, options);
  143.     }
  144.     public JsonSignature(JsonWebKey jsonWebKey, boolean secretKey, String signatureAlgorithm,
  145.             JwtHeaders jwtHeaders, JWSOptions options) throws UtilsException{
  146.         try {
  147.             org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
  148.             if(secretKey) {
  149.                 this.provider = JwsUtils.getSignatureProvider(jsonWebKey, algo);
  150.                 if(this.provider==null) {
  151.                     throw new UtilsException("(JsonWebKey) JwsSignatureProvider init failed; check signature algorithm ("+signatureAlgorithm+")");
  152.                 }
  153.             }else {
  154.                 this.provider = JwsUtils.getPrivateKeySignatureProvider(JwkUtils.toRSAPrivateKey(jsonWebKey), algo);
  155.             }
  156.             this.options=options;
  157.             this.jwtHeaders = jwtHeaders;
  158.         }catch(Exception t) {
  159.             throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.SENDER,t);
  160.         }
  161.     }

  162.     public JsonSignature(String secret,  String signatureAlgorithm, JWSOptions options) throws UtilsException{
  163.         this(secret, signatureAlgorithm, null, options);
  164.     }
  165.     public JsonSignature(String secret,  String signatureAlgorithm, JwtHeaders jwtHeaders, JWSOptions options) throws UtilsException{
  166.         try {
  167.            
  168.             org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
  169.             this.provider = JwsUtils.getHmacSignatureProvider(secret.getBytes(), algo);
  170.             if(this.provider==null) {
  171.                 throw new UtilsException("(Secret) JwsSignatureProvider init failed; check signature algorithm ("+signatureAlgorithm+")");
  172.             }
  173.             this.options=options;
  174.             this.jwtHeaders = jwtHeaders;
  175.         }catch(Exception t) {
  176.             throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.SENDER,t);
  177.         }
  178.     }

  179.     public String sign(String jsonString) throws UtilsException{
  180.         try {
  181.             switch(this.options.getSerialization()) {
  182.                 case JSON: return signJson(jsonString);
  183.                 case COMPACT: return signCompact(jsonString);
  184.                 default: throw new UtilsException("Unsupported serialization '"+this.options.getSerialization()+"'");
  185.             }
  186.         }
  187.         catch(Exception t) {
  188.             throw JsonUtils.convert(this.options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.SENDER,t);
  189.         }
  190.     }

  191.     private String signCompact(String jsonString) throws Exception {
  192.         JwsHeaders headersInternal = new JwsHeaders(this.provider.getAlgorithm());
  193.         // utilizzabile solamente per JSON. Per COMPACT è sconsigliato poichè limitato nei caratteri, non utilizzabile quindi per un json https://tools.ietf.org/html/rfc7797#page-8
  194.         // Infatti JwsCompactProducer di cxf non lo implementa proprio se si va a vedere il codice, il metodo 'getUnsignedEncodedJws' utilizzato per comporre il jwt non prevede il caso di lasciarlo in chiaro
  195. /**     if(this.options.isPayloadEncoding()==false) {
  196. //          headers.setPayloadEncodingStatus(this.options.isPayloadEncoding()); // RFC: https://tools.ietf.org/html/rfc7797
  197. //      }*/
  198.         JwsCompactProducer jwsProducer = new JwsCompactProducer(headersInternal, jsonString, this.options.isDetached());
  199.         fillJwtHeaders(jwsProducer.getJwsHeaders(), this.provider.getAlgorithm());
  200.         return jwsProducer.signWith(this.provider);
  201.     }

  202.     private String signJson(String jsonString) throws Exception {
  203.         JwsJsonProducer jwsProducer = new JwsJsonProducer(jsonString, false, this.options.isDetached());
  204.         JwsHeaders headersInternal = new JwsHeaders(this.provider.getAlgorithm());
  205.         if(!this.options.isPayloadEncoding()) {
  206.             headersInternal.setPayloadEncodingStatus(this.options.isPayloadEncoding()); // RFC: https://tools.ietf.org/html/rfc7797
  207.         }
  208.         fillJwtHeaders(headersInternal, this.provider.getAlgorithm());
  209.         return jwsProducer.signWith(this.provider, headersInternal);
  210.     }

  211.    
  212.     private void fillJwtHeaders(JwsHeaders headers,
  213.             org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm signatureAlgo) throws Exception {
  214.         if(this.headers!=null &&
  215.             this.headers.asMap()!=null && !this.headers.asMap().isEmpty()) {
  216.             Iterator<String> itKeys = this.headers.asMap().keySet().iterator();
  217.             while (itKeys.hasNext()) {
  218.                 String key = itKeys.next();
  219.                 if(!headers.containsHeader(key)) {
  220.                     headers.setHeader(key, this.headers.getHeader(key));
  221.                 }
  222.             }
  223.         }
  224.         if(this.jwtHeaders!=null) {
  225.             this.jwtHeaders.fillJwsHeaders(headers, false, signatureAlgo.getJwaName());
  226.         }
  227.     }
  228. }