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

  21. import java.security.PrivateKey;
  22. import java.util.ArrayList;
  23. import java.util.Collections;
  24. import java.util.Enumeration;
  25. import java.util.HashMap;
  26. import java.util.List;
  27. import java.util.Map;

  28. import javax.security.auth.x500.X500Principal;

  29. import org.apache.xml.security.utils.RFC2253Parser;
  30. import org.openspcoop2.utils.Utilities;
  31. import org.openspcoop2.utils.UtilsException;
  32. import org.openspcoop2.utils.UtilsMultiException;
  33. import org.openspcoop2.utils.io.Base64Utilities;
  34. import org.openspcoop2.utils.io.HexBinaryUtilities;
  35. import org.openspcoop2.utils.resources.Charset;
  36. import org.slf4j.Logger;
  37. import org.springframework.web.util.UriUtils;

  38. /**
  39.  * CertificateUtils
  40.  *
  41.  * @author Poli Andrea (apoli@link.it)
  42.  * @author $Author$
  43.  * @version $Rev$, $Date$
  44.  */
  45. public class CertificateUtils {
  46.    
  47.     private CertificateUtils() {}

  48.     public static void printCertificate(StringBuilder bf,List<java.security.cert.X509Certificate> certs){
  49.         printCertificate(bf, certs, false);
  50.     }
  51.     public static void printCertificate(StringBuilder bf,List<java.security.cert.X509Certificate> certs, boolean addPrefix){
  52.         if(!certs.isEmpty()) {
  53.             java.security.cert.X509Certificate[] certsArray = certs.toArray(new java.security.cert.X509Certificate[1]);
  54.             printCertificate(bf, certsArray, addPrefix);
  55.         }
  56.         else {
  57.             bf.append("X509Certificates: "+0+"\n");
  58.         }
  59.     }
  60.    
  61.     public static void printCertificate(StringBuilder bf,java.security.cert.X509Certificate[] certs){
  62.         printCertificate(bf, certs, false);
  63.     }
  64.     public static void printCertificate(StringBuilder bf,java.security.cert.X509Certificate[] certs, boolean addPrefix){
  65.         bf.append("X509Certificates: "+certs.length+"\n");
  66.         for (int i = 0; i < certs.length; i++) {
  67.             java.security.cert.X509Certificate cert = certs[i];
  68.             printCertificate(bf, cert, i+"", addPrefix);
  69.         }
  70.     }
  71.    
  72.     public static void printCertificate(StringBuilder bf,java.security.cert.X509Certificate cert, String name){
  73.         printCertificate(bf, cert, name, false);
  74.     }
  75.     public static void printCertificate(StringBuilder bf,java.security.cert.X509Certificate cert, String name, boolean addPrefix){
  76.         String prefix = "";
  77.         if(addPrefix) {
  78.             prefix = "Cert["+name+"].";
  79.         }
  80.         bf.append("#### X509Certificate["+name+"]\n");
  81.         bf.append("\t"+prefix+"toString()="+cert.toString()+"\n");
  82.         bf.append("\t"+prefix+"getType()="+cert.getType()+"\n");
  83.         bf.append("\t"+prefix+"getVersion()="+cert.getVersion()+"\n");
  84.        
  85.         if(cert.getIssuerDN()!=null){
  86.             bf.append("\t"+prefix+"cert.getIssuerDN().toString()="+cert.getIssuerDN().toString()+"\n");
  87.             bf.append("\t"+prefix+"cert.getIssuerDN().getName()="+cert.getIssuerDN().getName()+"\n");
  88.         }
  89.         else{
  90.             bf.append("\t"+prefix+"cert.getIssuerDN() is null"+"\n");
  91.         }
  92.        
  93.         if(cert.getIssuerX500Principal()!=null){
  94.             bf.append("\t"+prefix+"getIssuerX500Principal().toString()="+cert.getIssuerX500Principal().toString()+"\n");
  95.             bf.append("\t"+prefix+"getIssuerX500Principal().getName()="+cert.getIssuerX500Principal().getName()+"\n");
  96.             bf.append("\t"+prefix+"getIssuerX500Principal().getName(X500Principal.CANONICAL)="+cert.getIssuerX500Principal().getName(X500Principal.CANONICAL)+"\n");
  97.             bf.append("\t"+prefix+"getIssuerX500Principal().getName(X500Principal.RFC1779)="+cert.getIssuerX500Principal().getName(X500Principal.RFC1779)+"\n");
  98.             bf.append("\t"+prefix+"getIssuerX500Principal().getName(X500Principal.RFC2253)="+cert.getIssuerX500Principal().getName(X500Principal.RFC2253)+"\n");
  99.             /** Map<String,String> oidMapCanonical = new HashMap<>();
  100.                 bf.append("\t"+prefix+"getIssuerX500Principal().getName(X500Principal.CANONICAL,oidMapCanonical)="+
  101.                         cert.getIssuerX500Principal().getName(X500Principal.CANONICAL,oidMapCanonical));
  102.                 if(oidMapCanonical!=null && oidMapCanonical.size()>0){
  103.                     Iterator<String> it = oidMapCanonical.keySet().iterator();
  104.                     while (it.hasNext()) {
  105.                         String key = (String) it.next();
  106.                         String value = oidMapCanonical.get(key);
  107.                         bf.append("\t"+prefix+"getIssuerX500Principal() ["+key+"]=["+value+"]"+"\n");
  108.                     }
  109.                 }*/
  110.         }
  111.         else{
  112.             bf.append("\t"+prefix+"cert.getIssuerX500Principal() is null"+"\n");
  113.         }
  114.        
  115.         if(cert.getSubjectDN()!=null){
  116.             bf.append("\t"+prefix+"getSubjectDN().toString()="+cert.getSubjectDN().toString()+"\n");
  117.             bf.append("\t"+prefix+"getSubjectDN().getName()="+cert.getSubjectDN().getName()+"\n");
  118.         }
  119.         else{
  120.             bf.append("\t"+prefix+"cert.getSubjectDN() is null"+"\n");
  121.         }
  122.        
  123.         bf.append("\t"+prefix+"getSerialNumber()="+cert.getSerialNumber()+"\n");
  124.         bf.append("\t"+prefix+"getNotAfter()="+cert.getNotAfter()+"\n");
  125.         bf.append("\t"+prefix+"getNotBefore()="+cert.getNotBefore()+"\n");
  126.        
  127.         if(cert.getSubjectX500Principal()!=null){
  128.             bf.append("\t"+prefix+"getSubjectX500Principal().toString()="+cert.getSubjectX500Principal().toString()+"\n");
  129.             bf.append("\t"+prefix+"getSubjectX500Principal().getName()="+cert.getSubjectX500Principal().getName()+"\n");
  130.             bf.append("\t"+prefix+"getSubjectX500Principal().getName(X500Principal.CANONICAL)="+cert.getSubjectX500Principal().getName(X500Principal.CANONICAL)+"\n");
  131.             bf.append("\t"+prefix+"getSubjectX500Principal().getName(X500Principal.RFC1779)="+cert.getSubjectX500Principal().getName(X500Principal.RFC1779)+"\n");
  132.             bf.append("\t"+prefix+"getSubjectX500Principal().getName(X500Principal.RFC2253)="+cert.getSubjectX500Principal().getName(X500Principal.RFC2253)+"\n");
  133.             /** Map<String,String> oidMapCanonical = new HashMap<>();
  134.                 bf.append("\t"+prefix+"getSubjectX500Principal().getName(X500Principal.CANONICAL,oidMapCanonical)="+
  135.                         cert.getSubjectX500Principal().getName(X500Principal.CANONICAL,oidMapCanonical));
  136.                 if(oidMapCanonical!=null && oidMapCanonical.size()>0){
  137.                     Iterator<String> it = oidMapCanonical.keySet().iterator();
  138.                     while (it.hasNext()) {
  139.                         String key = (String) it.next();
  140.                         String value = oidMapCanonical.get(key);
  141.                         bf.append("\t"+prefix+"getSubjectX500Principal() ["+key+"]=["+value+"]"+"\n");
  142.                     }
  143.                 }*/
  144.         }
  145.         else{
  146.             bf.append("\t"+prefix+"cert.getSubjectX500Principal() is null"+"\n");
  147.         }
  148.     }
  149.    
  150.    
  151.    
  152.    
  153.    
  154.    
  155.     /* UTILITY SSL */
  156.    
  157.     private static final boolean TRIM_VALUE_BEFORE_SAVE_DB = true;
  158.    
  159.     private static void debug(Logger log, String msg) {
  160.         if(log!=null) {
  161.             log.debug(msg);
  162.         }
  163.     }
  164.     private static String getAnalisiTypePrefixString(String principal, PrincipalType type) {
  165.         return "("+principal+") Analisi "+type+" ";
  166.     }
  167.    
  168.     public static boolean sslVerify(String principalPresenteNellaConfigurazione, String principalArrivatoConnessioneSSL, PrincipalType type, Logger log) throws UtilsException{

  169.         if(log!=null) {
  170.             debug(log, "SSL VERIFY CONF["+principalPresenteNellaConfigurazione+"] SSL["+principalArrivatoConnessioneSSL+"]");
  171.         }
  172.         /** System.out.println("SSL VERIFY CONF["+principalPresenteNellaConfigurazione+"] SSL["+principalArrivatoConnessioneSSL+"]"); */

  173.         // Costruzione key=value
  174.         Map<String, List<String>> hashPrincipalArrivatoConnessioneSSL = CertificateUtils.getPrincipalIntoMap(principalArrivatoConnessioneSSL, type);
  175.         Map<String, List<String>> hashPrincipalPresenteNellaConfigurazione = CertificateUtils.getPrincipalIntoMap(principalPresenteNellaConfigurazione, type);

  176.         if(!sslVerifyCheckSize(principalPresenteNellaConfigurazione, principalArrivatoConnessioneSSL, type,
  177.                 hashPrincipalArrivatoConnessioneSSL, hashPrincipalPresenteNellaConfigurazione, log)) {
  178.             return false;
  179.         }

  180.         for (Map.Entry<String,List<String>> entry : hashPrincipalArrivatoConnessioneSSL.entrySet()) {

  181.             String key = entry.getKey();
  182.            
  183.             if(!hashPrincipalPresenteNellaConfigurazione.containsKey(key)){
  184.                 /** System.out.println("KEY ["+key+"] non presente"); */
  185.                 if(log!=null) {
  186.                     List<String> lKeys = new ArrayList<>();
  187.                     lKeys.addAll(hashPrincipalPresenteNellaConfigurazione.keySet());
  188.                     debug(log, "sslVerify key["+key+"] non trovata in "+type+" "+"Configurazione["+principalPresenteNellaConfigurazione+"]"+", key riscontrate: "+
  189.                             lKeys);
  190.                 }
  191.                 return false;
  192.             }

  193.             // Prendo valori
  194.             List<String> connessioneSSLValueList = hashPrincipalArrivatoConnessioneSSL.get(key);
  195.             List<String> configurazioneInternaValueList = hashPrincipalPresenteNellaConfigurazione.get(key);
  196.             if(connessioneSSLValueList.size() != configurazioneInternaValueList.size()){
  197.                 /** System.out.println("LUNGHEZZA DIVERSA KEY ["+key+"]"); */
  198.                 if(log!=null) {
  199.                     debug(log, "sslVerify "+type+" key["+key+"] trovata in Configurazione["+principalPresenteNellaConfigurazione+"]("+configurazioneInternaValueList.size()+
  200.                         ") SSL["+principalArrivatoConnessioneSSL+"]("+connessioneSSLValueList.size()+"): lunghezza differente");
  201.                 }
  202.                 return false;
  203.             }
  204.            
  205.             // Ordino Valori
  206.             Collections.sort(connessioneSSLValueList);
  207.             Collections.sort(configurazioneInternaValueList);
  208.            
  209.             // confronto valori
  210.             if(!sslVerifyCheckValues(key, type, connessioneSSLValueList, configurazioneInternaValueList, log)) {
  211.                 return false;
  212.             }
  213.            
  214.         }

  215.         /** System.out.println("SSL RETURN TRUE"); */
  216.         return true;

  217.     }
  218.     private static boolean sslVerifyCheckSize(String principalPresenteNellaConfigurazione, String principalArrivatoConnessioneSSL, PrincipalType type,
  219.             Map<String, List<String>> hashPrincipalArrivatoConnessioneSSL, Map<String, List<String>> hashPrincipalPresenteNellaConfigurazione, Logger log) {
  220.         if(hashPrincipalArrivatoConnessioneSSL.size() != hashPrincipalPresenteNellaConfigurazione.size()){
  221.             /** System.out.println("LUNGHEZZA DIVERSA"); */
  222.             if(log!=null) {
  223.                 debug(log, "sslVerify "+type+" "+"Configurazione["+principalPresenteNellaConfigurazione+"]"+"("+hashPrincipalPresenteNellaConfigurazione.size()+
  224.                     ") SSL["+principalArrivatoConnessioneSSL+"]("+hashPrincipalArrivatoConnessioneSSL.size()+"): lunghezza differente");
  225.             }
  226.             return false;
  227.         }
  228.         return true;
  229.     }
  230.     private static boolean sslVerifyCheckValues(String key, PrincipalType type, List<String> connessioneSSLValueList, List<String> configurazioneInternaValueList, Logger log) {
  231.         for (int i = 0; i < connessioneSSLValueList.size(); i++) {
  232.             String connessioneSSLValue = connessioneSSLValueList.get(i);
  233.             String configurazioneInternaValue = configurazioneInternaValueList.get(i);
  234.            
  235.             // Normalizzo caratteri escape
  236.             while(connessioneSSLValue.contains("\\/")){
  237.                 connessioneSSLValue = connessioneSSLValue.replace("\\/", "/");
  238.             }
  239.             while(connessioneSSLValue.contains("\\,")){
  240.                 connessioneSSLValue = connessioneSSLValue.replace("\\,", ",");
  241.             }
  242.             while(configurazioneInternaValue.contains("\\/")){
  243.                 configurazioneInternaValue = configurazioneInternaValue.replace("\\/", "/");
  244.             }
  245.             while(configurazioneInternaValue.contains("\\,")){
  246.                 configurazioneInternaValue = configurazioneInternaValue.replace("\\,", ",");
  247.             }
  248.            
  249.             if(!connessioneSSLValue.equals(configurazioneInternaValue)){
  250.                 if(log!=null) {
  251.                     debug(log, "sslVerify key["+key+"] "+type+" Configurazione["+configurazioneInternaValue+"] SSL["+connessioneSSLValue+"] not match");
  252.                 }
  253.                 /** System.out.println("VALUE SSL["+connessioneSSLValue+"]=CONF["+configurazioneInternaValue+"] non match"); */
  254.                 return false;
  255.             }
  256.         }
  257.         return true;
  258.     }


  259.     public static String formatPrincipal(String principal, PrincipalType type) throws UtilsException{

  260.         /** System.out.println("PRIMA ["+principal+"]"); */
  261.        
  262.         // Autenticazione SSL deve essere LIKE
  263.         Map<String, List<String>> hashPrincipal = CertificateUtils.getPrincipalIntoMap(principal, type);
  264.         StringBuilder bf = new StringBuilder();
  265.         bf.append("/");
  266.         for (Map.Entry<String,List<String>> entry : hashPrincipal.entrySet()) {
  267.            
  268.             String key = entry.getKey();
  269.            
  270.             List<String> listValues = hashPrincipal.get(key);
  271.             for (String value : listValues) {
  272.                 bf.append(CertificateUtils.formatKeyPrincipal(key));
  273.                 bf.append("=");
  274.                 bf.append(CertificateUtils.formatValuePrincipal(value));
  275.                 bf.append("/");
  276.             }
  277.            
  278.         }
  279.         /** System.out.println("DOPO ["+bf.toString()+"]"); */
  280.         return bf.toString();
  281.     }
  282.    
  283.     public static Map<String, String> formatPrincipalToMap(String principal, PrincipalType type) throws UtilsException{
  284.         Map<String, String> returnMap = new HashMap<>();
  285.         Map<String, List<String>> hashPrincipal = CertificateUtils.getPrincipalIntoMap(principal, type);
  286.         for (Map.Entry<String,List<String>> entry : hashPrincipal.entrySet()) {
  287.            
  288.             String key = entry.getKey();
  289.            
  290.             List<String> listValues = hashPrincipal.get(key);
  291.             for (String value : listValues) {
  292.                 String keyFormat = CertificateUtils.formatKeyPrincipal(key);
  293.                 String valueFormat = CertificateUtils.formatValuePrincipal(value);
  294.                 returnMap.put(keyFormat, valueFormat);
  295.             }
  296.            
  297.         }
  298.         return returnMap;
  299.     }

  300.     public static void validaPrincipal(String principalParam, PrincipalType type) throws UtilsException{
  301.        
  302.         /** System.out.println("PRIMA VALIDAZIONE ["+principalParam+"]"); */
  303.        
  304.         String principal = principalParam;
  305.         UtilsException normalizedException = null;
  306.         try{
  307.             String tmp = normalizePrincipal(principalParam);
  308.             principal = tmp;
  309.         }catch(UtilsException e){
  310.             /** System.out.println("ERRORE: "+e.getMessage());
  311.             // non voglio rilanciare l'eccezione, verra' segnalata l'eccezione puntuale.
  312.             // Se cosi' non fosse solo in fondo viene sollevata l'eccezione. */
  313.             normalizedException = e;
  314.         }
  315.        
  316.         /** System.out.println("DOPOP VALIDAZIONE ["+principal+"]"); */
  317.        
  318.         boolean commaFound = contains(principal, ",");
  319.         boolean slashFound = contains(principal, "/");
  320.         if(commaFound && slashFound){
  321.             throw new UtilsException("("+principal+") Non possono coesistere i separatore \",\" e \"/\", solo uno dei due tipi deve essere utilizzato come delimitatore (usare eventualmente come carattere di escape '\\')");
  322.         }
  323.         if(!commaFound && !slashFound && !principal.contains("=")){
  324.             throw new UtilsException("("+principal+") "+type+" non valido, nemmeno una coppia nome=valore trovata");
  325.         }
  326.         String [] valoriPrincipal = CertificateUtils.getValoriPrincipal(principal, type);
  327.         validaPrincipal(valoriPrincipal, principal, type);
  328.        
  329.         if(normalizedException!=null){
  330.             throw normalizedException;
  331.         }
  332.     }
  333.     private static void validaPrincipal(String [] valoriPrincipal, String principal, PrincipalType type) throws UtilsException {
  334.         boolean campoObbligatorioCN = false;
  335.         boolean campoObbligatorioOU = false;
  336.         boolean campoObbligatorioO = false;
  337.         boolean campoObbligatorioL = false;
  338.         boolean campoObbligatorioST = false;
  339.         boolean campoObbligatorioC = false;
  340.         boolean campoObbligatorioE = false;
  341.         for(int i=0; i<valoriPrincipal.length; i++){
  342.            
  343.             String [] keyValue = getKeyValuePairEngine(valoriPrincipal[i], principal, type);

  344.             if(keyValue[0].trim().contains(" ")){
  345.                 throw new UtilsException(getAnalisiTypePrefixString(principal, type)+"fallita: il campo ["+valoriPrincipal[i]+"] contiene spazi nella chiave identificativa ["+keyValue[0].trim()+"]");
  346.             }
  347.            
  348.             if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("CN")){
  349.                 campoObbligatorioCN = true;
  350.             }
  351.             else if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("OU")){
  352.                 campoObbligatorioOU = true;
  353.             }
  354.             else if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("O")){
  355.                 campoObbligatorioO = true;
  356.             }
  357.             else if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("L")){
  358.                 campoObbligatorioL = true;
  359.             }
  360.             else if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("ST")){
  361.                 campoObbligatorioST = true;
  362.             }
  363.             else if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("C")){
  364.                 campoObbligatorioC = true;
  365.             }
  366.             else if(CertificateUtils.formatKeyPrincipal(keyValue[0]).equalsIgnoreCase("E")){
  367.                 campoObbligatorioE = true;
  368.             }
  369.         }
  370.         if(!campoObbligatorioCN && !campoObbligatorioOU && !campoObbligatorioO && !campoObbligatorioL && !campoObbligatorioST && !campoObbligatorioC && !campoObbligatorioE){
  371.             throw new UtilsException("("+principal+") Almeno un attributo di certificato tra 'CN', 'OU', 'O', 'L', 'ST', 'C' e 'E' deve essere valorizzato.");
  372.         }
  373.     }

  374.    
  375.     public static String normalizePrincipal(String principalParam) throws UtilsException{
  376.        
  377.         /*
  378.          *  The principal extract from class represents an X.500 Principal.
  379.          *  X500Principals are represented by distinguished names such as "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US".
  380.          *  This class can be instantiated by using a string representation of the distinguished name, or by using the ASN.1 DER encoded byte representation
  381.          *  of the distinguished name.
  382.          *  The current specification for the string representation of a distinguished name is defined in RFC 2253:
  383.          *  Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names.
  384.          *  This class, however, accepts string formats from both RFC 2253 and RFC 1779: A String Representation of Distinguished Names,
  385.          *  and also recognizes attribute type keywords whose OIDs (Object Identifiers) are defined in RFC 3280:
  386.          *  Internet X.509 Public Key Infrastructure Certificate and CRL Profile.
  387.          */
  388.        
  389.         try{
  390.             // Normalizza da un eventuale formato human readable o da un formato RFC 1779 nel formato RFC 2253 gestito poi nel seguito del codice.
  391.             // Alcuni esempi dove il carattere '/' viene usato internamente come valore del CN
  392.             // Esempio di formato "human readable": /C=IT/ST=Italiy/OU=PROVA di Bari, di Como/CN=SPC/SOGGETTO
  393.             // Esempio di formato "RFC 1779": CN=SPC/SOGGETTO, OU="PROVA di Bari, di Como", ST=Italiy, C=IT
  394.             // Esempio di formato "RFC 2253": CN=SPC/SOGGETTO,OU=PROVA di Bari\, di Como,ST=Italiy,C=IT
  395.             return RFC2253Parser.normalize(principalParam);
  396.             /**String principal = RFC2253Parser.normalize(principalParam);
  397.             System.out.println(" ORIGINALE ["+principalParam+"]  NORMALIZZATO ["+principal+"]");
  398.             return principal;*/
  399.         }catch(Exception e){
  400.             throw new UtilsException("("+principalParam+") Normalizzazione RFC2253 non riuscita: "+e.getMessage(),e);
  401.         }
  402.     }

  403.     public static String [] getValoriPrincipal(String principalParam, PrincipalType type) throws UtilsException{
  404.         try{
  405.             /** System.out.println("PRINCIPAL getValoriPrincipal["+principalParam+"]"); */
  406.             String principal = normalizePrincipal(principalParam);
  407.             /** System.out.println("PRINCIPAL dopo normalize getValoriPrincipal["+principal+"]"); */
  408.            
  409.             return getValoriPrincipalEngine(principal, type);
  410.            
  411.         }catch(Exception e){
  412.            
  413.             /** System.out.println("PRINCIPAL getValoriPrincipal["+principalParam+"] errore: "+e.getMessage()); */
  414.            
  415.             try{
  416.            
  417.                 javax.naming.ldap.LdapName prova = new javax.naming.ldap.LdapName(principalParam);
  418.                 Enumeration<String> ens = prova.getAll();
  419.                 List<String> values = new ArrayList<>();
  420.                 while (ens.hasMoreElements()) {
  421.                     String name = ens.nextElement();
  422.                     values.add(name);
  423.                 }
  424.                
  425.                 if(!values.isEmpty()){
  426.                     return values.toArray(new String[1]);
  427.                 }
  428.                 else{
  429.                     throw new UtilsException("Coppie nome/valore non trovate");
  430.                 }
  431.                
  432.             }catch(Exception e2Level){
  433.                 /**e2Level.printStackTrace(System.out); */
  434.                 throw new UtilsException("("+principalParam+") javax.naming.ldap.LdapName reader failed: "+e2Level.getMessage()+". \nFirst method error: "+e.getMessage(),e);
  435.             }
  436.                
  437.         }
  438.     }
  439.        
  440.     private static String [] getValoriPrincipalEngine(String principal, PrincipalType type) throws UtilsException{
  441.            
  442.         String [] valori;
  443.         boolean commaFound = contains(principal, ",");
  444.         boolean slashFound = contains(principal, "/");
  445.         /**System.out.println("PRINCIPAL _getValoriPrincipal commaFound["+commaFound+"] slashFound["+slashFound+"]"); */
  446.         if(commaFound){
  447.             if(principal.startsWith(",")){
  448.                 principal = principal.substring(1);
  449.             }
  450.             if(principal.endsWith(",")){
  451.                 principal = principal.substring(0,principal.length()-1);
  452.             }
  453.             /**System.out.println("PRINCIPAL _getValoriPrincipal preSplit , ["+principal+"] ..");*/
  454.             valori = Utilities.split(principal, ',');
  455.         }else{
  456.             valori = getValoriPrincipalEngine(principal, type, slashFound);
  457.         }
  458.         if(valori==null || valori.length<1){
  459.             throw new UtilsException(getAnalisiTypePrefixString(principal, type)+"interno alla configurazione di OpenSPCoop non riuscita: null??");
  460.         }
  461.        
  462.         // validazione
  463.         for(int i=0; i<valori.length; i++){
  464.             getKeyValuePairEngine(valori[i], principal, type);
  465.         }
  466.                
  467.         return valori;
  468.     }
  469.     private static String [] getValoriPrincipalEngine(String principal, PrincipalType type, boolean slashFound) throws UtilsException {
  470.         /**System.out.println("PRINCIPAL _getValoriPrincipal comma not found ["+principal+"] .."); */
  471.         String [] valori = null;
  472.         if(!slashFound){
  473.             /** System.out.println("PRINCIPAL _getValoriPrincipal slash not found ["+principal+"] .."); */
  474.             int indexOf = principal.indexOf("=");
  475.             if(indexOf<=0){
  476.                 throw new UtilsException("("+principal+") Separatore validi per il "+type+" interno alla configurazione di OpenSPCoop non trovati:  \",\" o \"/\" e carattere \"=\" non presente");
  477.             }
  478.             if(principal.indexOf("=",indexOf+1)>=0){
  479.                 throw new UtilsException("("+principal+") Separatore validi per il "+type+" interno alla configurazione di OpenSPCoop non trovati:  \",\" o \"/\"");
  480.             }
  481.             valori =  new String[1];
  482.             valori[0] = principal;
  483.         }else{
  484.             valori = getValoriSlashPrincipalEngine(principal);
  485.         }
  486.         return valori;
  487.     }
  488.     private static String [] getValoriSlashPrincipalEngine(String principal) throws UtilsException {
  489.         if(principal.startsWith("/")){
  490.             principal = principal.substring(1);
  491.         }
  492.         if(principal.endsWith("/")){
  493.             principal = principal.substring(0,principal.length()-1);
  494.         }
  495.         /**System.out.println("PRINCIPAL _getValoriPrincipal preSplit / ["+principal+"] .."); */
  496.         String [] tmpValori = Utilities.split(principal, '/');
  497.        
  498.         // Bug Fix OP-670 certificato formato come:
  499.         // C=IT/ST= /O=Esempio di Agenzia/OU=Servizi Informatici/CN=Ministero dell'Interno/prova/23234234554/DEMO
  500.         List<String> normalize = new ArrayList<>();
  501.         StringBuilder bf = new StringBuilder();
  502.         for (String tmp : tmpValori) {
  503.             if(tmp.contains("=")) {
  504.                 if(bf.length()>0) {
  505.                     normalize.add(bf.toString());
  506.                     bf.delete(0, bf.length());
  507.                 }
  508.                 bf.append(tmp);
  509.             }
  510.             else {
  511.                 bf.append("/").append(tmp);
  512.             }
  513.         }
  514.         if(bf.length()>0) {
  515.             normalize.add(bf.toString());
  516.             bf.delete(0, bf.length());
  517.         }
  518.         return normalize.toArray(new String[1]);
  519.     }
  520.    
  521.     public static Map<String, List<String>> getPrincipalIntoMap(String principal, PrincipalType type) throws UtilsException{
  522.         Map<String, List<String>> hashPrincipal = new HashMap<>();
  523.         String [] valoriPrincipal = CertificateUtils.getValoriPrincipal(principal, type);
  524.         for(int i=0; i<valoriPrincipal.length; i++){
  525.            
  526.             // override eccezione in caso '=' non rpesente
  527.             if(!valoriPrincipal[i].contains("=")){
  528.                 String fallita = "fallita"+": ["+valoriPrincipal[i]+"] ";
  529.                 throw new UtilsException(getAnalisiTypePrefixString(principal, type)+fallita+"non separata dal carattere \"=\"");
  530.             }
  531.             String [] keyValue = getKeyValuePairEngine(valoriPrincipal[i], principal, type);
  532.            
  533.             /**System.out.println("CONF INTERNA ["+Utilities.formatKeyPrincipal(keyValue[0])+"] ["+Utilities.formatValuePrincipal(keyValue[1])+"]");*/
  534.             String formatKey = CertificateUtils.formatKeyPrincipal(keyValue[0]);
  535.             String formatValue = CertificateUtils.formatValuePrincipal(keyValue[1]);
  536.             List<String> listValue = null;
  537.             if(hashPrincipal.containsKey(formatKey)) {
  538.                 listValue = hashPrincipal.get(formatKey);
  539.             }
  540.             else {
  541.                 listValue = new ArrayList<>();
  542.                 hashPrincipal.put(formatKey, listValue);
  543.             }
  544.             listValue.add(formatValue);
  545.         }
  546.         return hashPrincipal;
  547.     }

  548.     public static String formatKeyPrincipal(String keyPrincipal){
  549.         return keyPrincipal.trim().toLowerCase();
  550.     }
  551.     public static String formatValuePrincipal(String valuePrincipal){
  552.         // siccome uso il carattere '/' come separatore, un eventuale '/' deve essere escaped.
  553.         StringBuilder bf = new StringBuilder();
  554.         for (int i = 0; i < valuePrincipal.length(); i++) {
  555.             if(valuePrincipal.charAt(i)=='/'){
  556.                 // escape
  557.                 if(i==0){
  558.                     bf.append('\\');
  559.                 }
  560.                 else{
  561.                     // verifico se non ho gia' effettuato l'escape
  562.                     if(valuePrincipal.charAt((i-1))!='\\'){
  563.                         bf.append('\\');
  564.                     }
  565.                 }
  566.             }
  567.             bf.append(valuePrincipal.charAt(i));
  568.         }
  569.         String value = bf.toString();
  570.         if(TRIM_VALUE_BEFORE_SAVE_DB) {
  571.             value = value.trim();
  572.         }
  573.         return value;
  574.     }

  575.     private static String[] getKeyValuePairEngine(String keyValue, String principal, PrincipalType type) throws UtilsException {
  576.        
  577.         if(!keyValue.contains("=")){
  578.             throw new UtilsException(getAnalisiTypePrefixString(principal, type)+"fallita: ["+keyValue+"] non separata dal carattere \"=\". Verificare che non esistano coppie che possiedono valori che contengono il carattere separatore (usare eventualmente come carattere di escape '\\')");
  579.         }
  580.        
  581.         // Bug Fix OP-664
  582.         // Per non bruciare gli spazi presenti nei valori della chiave che sono ammessi. Ad esempio e' capitato un "C=IT,ST= ,CN=XXESEMPIOXX"
  583.         /**String [] keyValue = keyValue.trim().split("=");*/
  584.         String [] keyValueReturn = keyValue.split("="); // lo spazio รจ ammesso nel valore.      
  585.         if(keyValueReturn.length<2){
  586.             if(TRIM_VALUE_BEFORE_SAVE_DB && keyValueReturn.length==1) {
  587.                 // Serve per i valori inseriti che poi vengono comunque normalizzati con il trim. Se il valore era ' ' viene normalizzato a ''
  588.                 // una volta riletto poi si ottiene l'errore.
  589.                 // Che ci sia l'uguale e' garantito dai controlli sopra. In pratica keyValue.endsWith("=")
  590.                 String key = keyValueReturn[0];
  591.                 keyValueReturn = new String[2];
  592.                 keyValueReturn[0] = key;
  593.                 keyValueReturn[1] = "";
  594.             }
  595.             else {
  596.                 throw new UtilsException(getAnalisiTypePrefixString(principal, type)+"fallita: ["+keyValue+"] non contiene un valore? Verificare che non esistano coppie che possiedono valori che contengono il carattere separatore (usare eventualmente come carattere di escape '\\')");
  597.             }
  598.         }
  599.        
  600.         // Lo spazio e' ammesso nel valore, ma non nella chiave
  601.         keyValueReturn[0] = keyValueReturn[0].trim();
  602.         /**System.out.println("KEY["+keyValueReturn[0]+"]=VALUE["+keyValueReturn[1]+"]");*/
  603.        
  604.         // Questo controllo non deve essere fatto poiche' il valore puo' contenere il '='.
  605. /**     if(keyValue.length!=2){
  606. //          throw new UtilsException(getAnalisiTypePrefixString(principal, type)+"fallita: ["+keyValue+"] contiene piu' di un carattere \"=\"");
  607. //      }*/
  608.         if(keyValueReturn.length==2) {
  609.             return keyValueReturn;
  610.         }
  611.         else {
  612.             String[] keyValueReturnNormalized = new String[2];
  613.             keyValueReturnNormalized[0] = keyValueReturn[0];
  614.             keyValueReturnNormalized[1] = extractValueFromKeyPairEngine(keyValue);
  615.             return keyValueReturnNormalized;
  616.         }
  617.        
  618.     }
  619.     private static String extractValueFromKeyPairEngine(String keyValue) throws UtilsException {
  620.         // Questo metodo server poiche' il valore puo' contenere il '=' ed il carattere ' ' anche all'inizio o alla fine.
  621.         // Quindi uno split con il carattere '=' non puo' essere usato.
  622.         // Deve quindi essere estratto dopo il primo uguale
  623.         int indexOf = keyValue.indexOf("=");
  624.         if(indexOf<=0) {
  625.             throw new UtilsException("Carattere '=' non presente in ["+keyValue+"]");
  626.         }
  627.         return keyValue.substring(indexOf+1);
  628.         /**String valoreEstratto = keyValue.substring(indexOf+1);
  629.         System.out.println("VALORE ESTRATTO ["+valoreEstratto+"]");
  630.         return valoreEstratto;*/
  631.     }
  632.    
  633.    
  634.     private static boolean contains(String value,String separator){
  635.         int indexOf = value.indexOf(separator);
  636.         boolean found = false;
  637.         if(indexOf==0){
  638.             found = true;
  639.         }
  640.         else{
  641.             boolean itera = true;
  642.             while(indexOf>0 && itera){
  643.                 char precedente = value.charAt(indexOf-1);
  644.                 if(precedente == '\\'){
  645.                     if(indexOf+1>value.length()){
  646.                         itera=false;
  647.                     }
  648.                     else{
  649.                         indexOf = value.indexOf(separator,indexOf+1);
  650.                     }
  651.                 }
  652.                 else{
  653.                     found = true;
  654.                     itera=false;
  655.                 }
  656.             }
  657.         }
  658.         return found;
  659.     }
  660.    
  661.     public static Certificate readCertificate(CertificateDecodeConfig config, String certificateParam) throws UtilsException {
  662.         return readCertificateEngine(config, certificateParam, Charset.UTF_8.getValue());
  663.     }
  664.     public static Certificate readCertificate(CertificateDecodeConfig config, String certificateParam, String charset) throws UtilsException {
  665.         return readCertificateEngine(config, certificateParam, charset);
  666.     }
  667.    
  668.     private static Certificate readCertificateEngine(CertificateDecodeConfig config, String certificateParam, String charset) throws UtilsException {
  669.         if(config.isUrlDecodeOrBase64Decode() || config.isUrlDecodeOrBase64DecodeOrHexDecode()) {
  670.            
  671.             Throwable tUrlDecode = null;
  672.             try {
  673.                 config.setUrlDecode(true);
  674.                 config.setBase64Decode(false);
  675.                 config.setHexDecode(false);
  676.                 return readCertificateEngineByConfig(config, certificateParam, charset);
  677.             }catch(Exception t) {
  678.                 tUrlDecode = t;
  679.             }
  680.            
  681.             Throwable tBase64Decode = null;
  682.             try {
  683.                 config.setUrlDecode(false);
  684.                 config.setBase64Decode(true);
  685.                 config.setHexDecode(false);
  686.                 return readCertificateEngineByConfig(config, certificateParam, charset);
  687.             }catch(Exception t) {
  688.                 tBase64Decode = t;
  689.             }
  690.            
  691.             Throwable tHexDecode = null;
  692.             if(config.isUrlDecodeOrBase64DecodeOrHexDecode()) {
  693.                 try {
  694.                     config.setUrlDecode(false);
  695.                     config.setBase64Decode(false);
  696.                     config.setHexDecode(true);
  697.                     return readCertificateEngineByConfig(config, certificateParam, charset);
  698.                 }catch(Exception t) {
  699.                     tHexDecode = t;
  700.                 }
  701.             }
  702.            
  703.             UtilsMultiException uMulti = config.isUrlDecodeOrBase64DecodeOrHexDecode() ?
  704.                 new UtilsMultiException("Decodifica non riuscita", tUrlDecode, tBase64Decode, tHexDecode)  
  705.                     :
  706.                 new UtilsMultiException("Decodifica non riuscita", tUrlDecode, tBase64Decode);
  707.             throw new UtilsException(uMulti.getMessage(),uMulti);
  708.         }
  709.         else {
  710.             return readCertificateEngineByConfig(config, certificateParam, charset);
  711.         }
  712.     }
  713.     private static Certificate readCertificateEngineByConfig(CertificateDecodeConfig config, String certificateParam, String charset) throws UtilsException {
  714.        
  715.         if(certificateParam==null || "".equals(certificateParam)){
  716.             throw new UtilsException("Certificate non fornito");
  717.         }
  718.        
  719.         try {
  720.        
  721.             String certificate = certificateParam;
  722.            
  723.             if(config.isUrlDecode()) {
  724.                 certificate = UriUtils.decode(certificate, charset);
  725.             }
  726.            
  727.             boolean forceEnrichPEMBeginEnd = false;
  728.             if(config.isReplace()) {
  729.                 StringBuilder sbNewCertificate = new StringBuilder();
  730.                 forceEnrichPEMBeginEnd = replaceCharacters(config, certificate, sbNewCertificate);
  731.                 certificate = sbNewCertificate.toString();
  732.             }
  733.            
  734.             if(config.isEnrichPEMBeginEnd() || forceEnrichPEMBeginEnd) {
  735.                 certificate = addPEMDeclaration(certificate, forceEnrichPEMBeginEnd);
  736.             }
  737.            
  738.             byte [] certBytes = null;
  739.             if(config.isBase64Decode()) {
  740.                 certBytes = Base64Utilities.decode(certificate);
  741.             }
  742.             else if(config.isHexDecode()) {
  743.                 certBytes = HexBinaryUtilities.decode(certificate);
  744.             }
  745.             else {
  746.                 certBytes = certificate.getBytes(charset);
  747.             }
  748.            
  749.             // Per adesso l'utility di load gestisce solo il tipo DER. La decodifica in base64 รจ quindi essenziale, a meno che non sia un DER.
  750.            
  751.             return ArchiveLoader.load(certBytes);
  752.            
  753.         }catch(Exception e) {
  754.             throw new UtilsException(e.getMessage(), e);
  755.         }
  756.        
  757.     }
  758.    
  759.     private static boolean replaceCharacters(CertificateDecodeConfig config, String certificate, StringBuilder sbNewCertificate) {
  760.         boolean forceEnrichPEMBeginEnd = false;
  761.         // per evitare di sovrascrivere 'BEGIN' e 'END'
  762.         if(certificate.startsWith(PEMReader.X509_BEGIN) && certificate.length()>PEMReader.X509_BEGIN.length()) {
  763.             certificate = certificate.substring(PEMReader.X509_BEGIN.length());
  764.             forceEnrichPEMBeginEnd = true;
  765.         }
  766.         if(certificate.endsWith(PEMReader.X509_END) && certificate.length()>PEMReader.X509_END.length()) {
  767.             certificate = certificate.substring(0, certificate.length()-PEMReader.X509_END.length());
  768.         }
  769.        
  770.         int index = 0; // per evitare bug di cicli infiniti
  771.         while(certificate.contains(config.getReplaceSource()) && index<10000) {
  772.             certificate = certificate.replace(config.getReplaceSource(), config.getReplaceDest());
  773.             index++;
  774.         }
  775.         sbNewCertificate.append(certificate);
  776.         return forceEnrichPEMBeginEnd;
  777.     }
  778.    
  779.     private static String addPEMDeclaration(String certificate, boolean forceEnrichPEMBeginEnd) {
  780.         if(!certificate.startsWith(PEMReader.X509_BEGIN)) {
  781.             certificate = PEMReader.X509_BEGIN+ (forceEnrichPEMBeginEnd?"":"\n")+ certificate;
  782.         }
  783.         if(!certificate.endsWith(PEMReader.X509_END)) {
  784.             certificate = certificate+ (forceEnrichPEMBeginEnd?"":"\n") +PEMReader.X509_END;
  785.         }
  786.         return certificate;
  787.     }
  788.    
  789.     public static String toPEM(java.security.cert.X509Certificate cert) throws UtilsException {
  790.         try {
  791.             java.io.StringWriter sw = new java.io.StringWriter();
  792.             try (org.bouncycastle.openssl.jcajce.JcaPEMWriter pw = new org.bouncycastle.openssl.jcajce.JcaPEMWriter(sw)) {
  793.                 pw.writeObject(cert);
  794.             }
  795.             sw.flush();
  796.             sw.close();
  797.             return sw.toString();
  798.         }catch(Exception e) {
  799.             throw new UtilsException(e.getMessage(), e);
  800.         }
  801.     }
  802.     public static String toPEM(PrivateKey privateKey) throws UtilsException {
  803.         try {
  804.             java.io.StringWriter sw = new java.io.StringWriter();
  805.             try (org.bouncycastle.openssl.jcajce.JcaPEMWriter pw = new org.bouncycastle.openssl.jcajce.JcaPEMWriter(sw)) {
  806.                 pw.writeObject(privateKey);
  807.             }
  808.             sw.flush();
  809.             sw.close();
  810.             return sw.toString();
  811.         }catch(Exception e) {
  812.             throw new UtilsException(e.getMessage(), e);
  813.         }
  814.     }
  815. }