X509Utils.java

  1. /*
  2.  * Copyright (C) 2020-2024 by Savoir-faire Linux
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 3 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  16.  */
  17. package net.jami.jams.common.utils;

  18. import com.google.gson.Gson;

  19. import lombok.extern.slf4j.Slf4j;

  20. import net.jami.jams.common.serialization.adapters.GsonFactory;
  21. import net.jami.jams.common.updater.subscription.LicenseInformation;

  22. import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
  23. import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
  24. import org.bouncycastle.openssl.PEMKeyPair;
  25. import org.bouncycastle.openssl.PEMParser;
  26. import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
  27. import org.bouncycastle.pkcs.PKCS10CertificationRequest;

  28. import java.io.BufferedReader;
  29. import java.io.ByteArrayInputStream;
  30. import java.io.InputStream;
  31. import java.io.InputStreamReader;
  32. import java.io.StringReader;
  33. import java.nio.charset.StandardCharsets;
  34. import java.security.KeyFactory;
  35. import java.security.PrivateKey;
  36. import java.security.PublicKey;
  37. import java.security.cert.CertificateFactory;
  38. import java.security.cert.X509Certificate;
  39. import java.security.spec.PKCS8EncodedKeySpec;
  40. import java.util.Base64;
  41. import java.util.HashMap;
  42. import java.util.Vector;

  43. import javax.naming.ldap.LdapName;
  44. import javax.naming.ldap.Rdn;

  45. @Slf4j
  46. public class X509Utils {

  47.     private static final String PVK_HEADER = "-----BEGIN PRIVATE KEY-----\n";
  48.     private static final String PVK_TAIL = "\n-----END PRIVATE KEY-----";
  49.     private static final String CERT_HEADER = "-----BEGIN CERTIFICATE-----\n";
  50.     private static final String CERT_TAIL = "\n-----END CERTIFICATE-----";
  51.     private static final String PPK_HEADER = "-----BEGIN PUBLIC KEY-----\n";
  52.     private static final String PPK_TAIL = "\n-----END PUBLIC KEY-----";

  53.     public static PrivateKey getKeyFromPEMString(String keyString) {
  54.         if (keyString.isEmpty()) return null;

  55.         try {
  56.             PEMParser parser = new PEMParser(new StringReader(keyString));
  57.             Object parsedObject = parser.readObject();
  58.             if (parsedObject instanceof PEMKeyPair) {
  59.                 PEMKeyPair pk = (PEMKeyPair) parsedObject;
  60.                 PKCS8EncodedKeySpec keySpec =
  61.                         new PKCS8EncodedKeySpec(pk.getPrivateKeyInfo().getEncoded());
  62.                 return KeyFactory.getInstance("RSA").generatePrivate(keySpec);
  63.             } else {
  64.                 JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
  65.                 return converter.getPrivateKey((PrivateKeyInfo) parsedObject);
  66.             }
  67.         } catch (Exception e) {
  68.             log.error(
  69.                     "An error occurred while converting the PEM to PrivateKey, stack trace: " + e);
  70.             return null;
  71.         }
  72.     }

  73.     public static PublicKey getPubKeyFromPEMString(String keyString) {
  74.         try {
  75.             PEMParser parser = new PEMParser(new StringReader(keyString));
  76.             Object parsedObject = parser.readObject();
  77.             if (parsedObject instanceof PEMKeyPair) {
  78.                 PEMKeyPair pk = (PEMKeyPair) parsedObject;
  79.                 PKCS8EncodedKeySpec keySpec =
  80.                         new PKCS8EncodedKeySpec(pk.getPublicKeyInfo().getEncoded());
  81.                 return KeyFactory.getInstance("RSA").generatePublic(keySpec);
  82.             } else {
  83.                 JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
  84.                 return converter.getPublicKey((SubjectPublicKeyInfo) parsedObject);
  85.             }
  86.         } catch (Exception e) {
  87.             log.error("An error occurred while reading the public key from string!");
  88.             return null;
  89.         }
  90.     }

  91.     public static X509Certificate getCertificateFromPEMString(String certificateString) {
  92.         try {
  93.             CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
  94.             InputStream inputStream = new ByteArrayInputStream(certificateString.getBytes());
  95.             return (X509Certificate) certificateFactory.generateCertificate(inputStream);
  96.         } catch (Exception e) {
  97.             log.error("An error occurred while converting the PEM to X509, stack trace: " + e);
  98.             return null;
  99.         }
  100.     }

  101.     public static String getPEMStringFromPrivateKey(PrivateKey privateKey) {
  102.         StringBuilder stringBuilder = new StringBuilder();
  103.         try {
  104.             stringBuilder.append(PVK_HEADER);
  105.             stringBuilder.append(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
  106.             stringBuilder.append(PVK_TAIL);
  107.             return stringBuilder.toString();
  108.         } catch (Exception e) {
  109.             log.error(
  110.                     "An error occurred while converting the Private Key to PEM, stack trace: " + e);
  111.             return null;
  112.         }
  113.     }

  114.     public static String getPEMStringFromCertificate(X509Certificate certificate) {
  115.         StringBuilder stringBuilder = new StringBuilder();
  116.         try {
  117.             stringBuilder.append(CERT_HEADER);
  118.             stringBuilder.append(Base64.getEncoder().encodeToString(certificate.getEncoded()));
  119.             stringBuilder.append(CERT_TAIL);
  120.             return stringBuilder.toString();
  121.         } catch (Exception e) {
  122.             log.error(
  123.                     "An error occurred while converting the Certificate Key to PEM, stack trace: "
  124.                             + e);
  125.             return null;
  126.         }
  127.     }

  128.     public static String getPEMStringFromPubKey(PublicKey publicKey) {
  129.         StringBuilder stringBuilder = new StringBuilder();
  130.         try {
  131.             stringBuilder.append(PPK_HEADER);
  132.             stringBuilder.append(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
  133.             stringBuilder.append(PPK_TAIL);
  134.             return stringBuilder.toString();
  135.         } catch (Exception e) {
  136.             log.error(
  137.                     "An error occurred while converting the Public Key to PEM, stack trace: " + e);
  138.             return null;
  139.         }
  140.     }

  141.     public static PKCS10CertificationRequest getCSRFromString(String pkcs10StringRequest) {
  142.         try {
  143.             ByteArrayInputStream pemStream =
  144.                     new ByteArrayInputStream(pkcs10StringRequest.getBytes(StandardCharsets.UTF_8));
  145.             PEMParser pemParser =
  146.                     new PEMParser(new BufferedReader(new InputStreamReader(pemStream)));
  147.             Object parsedObj = pemParser.readObject();
  148.             if (parsedObj instanceof PKCS10CertificationRequest)
  149.                 return (PKCS10CertificationRequest) parsedObj;
  150.             log.error("The request does not seem to be a CSR request!");
  151.             return null;
  152.         } catch (Exception e) {
  153.             log.error(
  154.                     "An error occurred while converting the string to PKCS10 Certification Request, stack trace: "
  155.                             + e);
  156.             return null;
  157.         }
  158.     }

  159.     public static Vector<Object> loadLicenseFromDatFile(String fileContents) {
  160.         Vector<Object> res = new Vector<>();
  161.         String keypair = new String(Base64.getDecoder().decode(fileContents));
  162.         int cutPoint = keypair.indexOf("-----BEGIN PRIVATE KEY-----");
  163.         String strCertificate = keypair.substring(0, cutPoint);
  164.         String strPrivateKey = keypair.substring(cutPoint);
  165.         res.add(getCertificateFromPEMString(strCertificate));
  166.         res.add(getKeyFromPEMString(strPrivateKey));
  167.         return res;
  168.     }

  169.     public static LicenseInformation extractSubscriptionTypeFromCertificate(
  170.             X509Certificate certificate) {
  171.         try {
  172.             LdapName ln = new LdapName(certificate.getSubjectDN().toString());
  173.             for (Rdn rdn : ln.getRdns()) {
  174.                 try {
  175.                     Gson gson = GsonFactory.createGson();
  176.                     byte[] bytes = Base64.getDecoder().decode(rdn.getValue().toString().getBytes());
  177.                     return gson.fromJson(new String(bytes, "US-ASCII"), LicenseInformation.class);
  178.                 } catch (Exception e) {
  179.                 }
  180.             }
  181.             return null;
  182.         } catch (Exception e) {
  183.             return null;
  184.         }
  185.     }

  186.     public static HashMap<String, String> extractDNFromCertificate(X509Certificate certificate)
  187.             throws Exception {
  188.         HashMap<String, String> subjectMap = new HashMap<>();
  189.         LdapName ln = new LdapName(certificate.getSubjectDN().toString());
  190.         for (Rdn rdn : ln.getRdns()) {
  191.             subjectMap.put(rdn.getType(), rdn.getValue().toString());
  192.         }
  193.         return subjectMap;
  194.     }
  195. }