From 52c77b2f12605aaf7b96e558c96a41905e5e83f4 Mon Sep 17 00:00:00 2001
From: Adil El Farissi <144741970+AdilElFarissi@users.noreply.github.com>
Date: Mon, 16 Sep 2024 01:52:19 +0000
Subject: [PATCH] Implementation of a basic PEM encoded to OpenSim compatible
PKCS12 certificates converter.
As you know most CAs return PEM encoded certificates and require a conversion using OpenSSL to make them compatible with OpenSim.
This implementation does the automatic conversion from .pem to .p12 and .pfx at OpenSim startup which updates the certificate in case of automatic certificates renewal...
Note: still under testing using certbot and Let's Encrypt certs...
Thank you and good continuaion
Web Rain :)
---
OpenSim/Framework/Util.cs | 47 +++++++++++++++++++++++
OpenSim/Region/Application/OpenSimBase.cs | 10 +++++
bin/OpenSim.ini.example | 27 ++++++++++++-
bin/OpenSimDefaults.ini | 27 +++++++++++++
4 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index bc5f08a09b..7ca7d93416 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1550,6 +1550,53 @@ namespace OpenSim.Framework
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ConvertPemToPKCS12(string certFileName, string fullChainPath, string privateKeyPath)
+ {
+ ConvertPemToPKCS12Certificate(certFileName, fullChainPath, privateKeyPath, null);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ConvertPemToPKCS12(string certFileName, string fullChainPath, string privateKeyPath, string outputPassword)
+ {
+ ConvertPemToPKCS12Certificate(certFileName, fullChainPath, privateKeyPath, outputPassword);
+ }
+
+ ///
+ /// Convert or renew .pem certificate to PKCS12 .pfx and .p12 usable by OpenSim.
+ /// the parameters are set in the startup section of OpenSim.ini
+ ///
+ /// The output certificate file name.
+ /// The path of fullchain.pem. If your CA don't provide
+ /// the fullchain file, you can set the cert.pem instead.
+ /// The path of the private key (privkey.pem).
+ /// The output certificates password.
+ private static void ConvertPemToPKCS12Certificate(string certFileName, string certPath, string keyPath, string outputPassword)
+ {
+ if(string.IsNullOrEmpty(certPath) || string.IsNullOrEmpty(keyPath)){
+ m_log.ErrorFormat("[UTIL]: Missing or invalid fullchain.pem / privkey.pem path!.");
+ return;
+ }
+ // Create the SSL folder and sub folders if not exists.
+ if (!Directory.Exists("SSL\\ssl\\"))
+ Directory.CreateDirectory("SSL\\ssl\\");
+
+ // Convert .pem (like Let's Encrypt files) to X509Certificate2 certificate.
+ X509Certificate2 certificate = X509Certificate2.CreateFromPemFile(certPath, keyPath);
+
+ // Export and store the .pfx and .p12 certificates in SSL\ssl\.
+ byte[] pfxCertBytes = string.IsNullOrEmpty(outputPassword)
+ ? certificate.Export(X509ContentType.Pfx)
+ : certificate.Export(X509ContentType.Pfx, outputPassword);
+ File.WriteAllBytes($"SSL\\ssl\\{certFileName}.pfx", pfxCertBytes);
+
+ byte[] p12CertBytes = string.IsNullOrEmpty(outputPassword)
+ ? certificate.Export(X509ContentType.Pkcs12)
+ : certificate.Export(X509ContentType.Pkcs12, outputPassword);
+ File.WriteAllBytes($"SSL\\ssl\\{certFileName}.p12", p12CertBytes);
+
+ }
+
public static int fast_distance2d(int x, int y)
{
x = Math.Abs(x);
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index 2971f61326..03c8fdd36f 100755
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -367,6 +367,16 @@ namespace OpenSim
);
}
}
+
+ if(startupConfig.GetBoolean("EnableCertConverter", false))
+ {
+ Util.ConvertPemToPKCS12(
+ string.IsNullOrEmpty(startupConfig.GetString("outputCertName")) ? "letsencrypt" : startupConfig.GetString("outputCertName"),
+ string.IsNullOrEmpty(startupConfig.GetString("PemCertPublicKey")) ? string.Empty : startupConfig.GetString("PemCertPublicKey"),
+ string.IsNullOrEmpty(startupConfig.GetString("PemCertPrivateKey")) ? string.Empty : startupConfig.GetString("PemCertPrivateKey"),
+ string.IsNullOrEmpty(startupConfig.GetString("outputCertPassword")) ? string.Empty : startupConfig.GetString("outputCertPassword")
+ );
+ }
if(m_networkServersInfo.HttpUsesSSL)
{
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index 4f49f1f46d..3c2ff30b47 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -309,12 +309,37 @@
;; default is false
; TelehubAllowLandmark = false
+
+ ;; Let's Encrypt and others ".pem" certificates converter settings.
+ ;; Enabling this feature will automatically convert the CA .pem certificates to
+ ;; OpenSim compatible PKCS12 .p12 and .pfx certificates on every server startup.
+ ;; The resulting certificates are stored in the bin\SSL\ssl folder.
+ ;# {EnableCertConverter} {} {Enable pem to pkcs12 certificates converter} {true false} false
+ EnableCertConverter = false
+
+ ;; Set the absolute path of the "fullchain.pem". If your CA don't provide this file,
+ ;; you can use the "cert.pem" instead.
+ ;# {PemCertPublicKey} {} {Set the path of the public key .pem} {} ""
+ PemCertPublicKey = ""
+
+ ;; Set the absolute path of the pem private key "privkey.pem".
+ ;# {PemCertPrivateKey} {} {Set the path of the private key .pem} {} ""
+ PemCertPrivateKey = ""
+
+ ;; Set the name of the resulting .p12 and .pfx.
+ ;# {outputCertName} {} {Set the name of the resulting .p12 and .pfx} {} "letsencrypt"
+ outputCertName = "letsencrypt"
+
+ ;; Set the .p12 and .pfx password.
+ ;# {outputCertPassword} {} {Set the .p12 and .pfx password} {} ""
+ outputCertPassword = ""
+
;; SSL selfsigned certificate settings.
;; Enable selfsigned certificate creation for local and external use. When set to true, will create a folder SSL\ and 2 sub folders SSL\ssl\ and SSL\src\.
;; Next creates and store an RSA private key in SSL\src\ and the derived selfsigned certificate in SSL\ssl\ folder.
;;Is also possible to renew the certificate on every server restart if CertRenewOnStartup is set to true.
- ;# {EnbleSelfsignedCertSupport} {} {Enable selfsigned certificate creation and renew} {true false} false
+ ;# {EnableSelfsignedCertSupport} {} {Enable selfsigned certificate creation and renew} {true false} false
EnableSelfsignedCertSupport = false
;; Renew the selfsigned certificate on every server startup ?
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index 57c20cd414..3da8fe1dd6 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -399,6 +399,33 @@
; routing and land at the landmark coordinates when set to true
; default is false
; TelehubAllowLandmark = false
+
+ ; #
+ ; # Let's Encrypt and others ".pem" certificates converter settings.
+ ; #
+
+ ;; Enabling this feature will automatically convert the CA .pem certificates to
+ ;; OpenSim compatible PKCS12 .p12 and .pfx certificates on every server startup.
+ ;; The resulting certificates are stored in the bin\SSL\ssl folder.
+ ;# {EnableCertConverter} {} {Enable pem to pkcs12 certificates converter} {true false} false
+ EnableCertConverter = false
+
+ ;; Set the absolute path of the "fullchain.pem". If your CA don't provide this file,
+ ;; you can use the "cert.pem" instead.
+ ;# {PemCertPublicKey} {} {Set the path of the public key .pem} {} ""
+ PemCertPublicKey = ""
+
+ ;; Set the absolute path of the pem private key "privkey.pem".
+ ;# {PemCertPrivateKey} {} {Set the path of the private key .pem} {} ""
+ PemCertPrivateKey = ""
+
+ ;; Set the name of the resulting .p12 and .pfx.
+ ;# {outputCertName} {} {Set the name of the resulting .p12 and .pfx} {} "letsencrypt"
+ outputCertName = "letsencrypt"
+
+ ;; Set the .p12 and .pfx password.
+ ;# {outputCertPassword} {} {Set the .p12 and .pfx password} {} ""
+ outputCertPassword = ""
; #
; # SSL selfsigned certificate settings.