B4J Library ABKeystoreSSL: SSL Certificate generator using Let's Encrypt

This library allows you to generate SSL Certificates without the need of using the openssl and keytool tools. It can also reload a certificate in a jServer app (also ABMServer or BANanoServer) at runtime (the reload is based on the code @OliverA provided).



Download: https://gorgeousapps.com/ABKeystoreSSL1.08b.zip


Save the generated user.key and domain.key in a secure place! IF YOU LOSE THEM, THERE IS NO WAY TO RECOVER YOUR ACCOUNT!

1. You must have a domain and a web server like Apache running on port 80!
2. Copy ALL the files in the .zip to your Additional Libraries folder
3. credentials created in developers mode will show in the browser as not correct, but still run in HTTP/2 mode. This is a Let's encrypt limitation.
Also, READ THE LOGS. There is important info in it (and you can ignore the ones about SLF4J, this is because I show the logs in the B4J pane instead)

ABKeystoreSSL

Author:
Alain Bailleul
Version: 1.07
  • ABKeystoreSSL
    • Functions:
      • GenerateJKS (myDomains As List, dir As String, keyStoreFileNameNoExtension As String, keyStorePassword As String, keyManagerPassword As String, forProduction As Boolean, vault As String, ForceGenerate As Boolean) As Boolean
        Generates a certificate for the given domains. Also takes care for the registration process.
        domains: Domains to get a common certificate for

        dir: folder where to put the generated keystore .jks file

        keyStoreFileNameNoExtension: name of your keystore file (without the .jks extension)

        keyStorePassword: the keyStore password you want to use in your jServer

        keyManagerPassword: the keyManager password you want to use in your jServer

        forProduction: if true uses "acme://letsencrypt.org", else "acme://letsencrypt.org/staging"

        vault: a folder name where your key files should be stored/retrieved.
        a submap with the keyStoreFileNameNoExtension will be created.

        ForceGenerate: if true then the jks file will be generated, ignoring the fact if may not be needed as it is still valid

        TIP: Use the GetNotBefore and GetNotAfter methods right after running fetchCertificate to find the valid date range.

        Those dates are also saved in your vault location as 'range.times'.
      • GenerateSelfSignedJKS (dir As String, keyStoreFileNameNoExtension As String, keyStorePassword As String, keyManagerPassword As String, vault As String) As Boolean
        Generates a self signed certificate for development purposes.

        dir: folder where to put the generated keystore .jks file

        keyStoreFileNameNoExtension: name of your keystore file (without the .jks extension)

        keyStorePassword: the keyStore password you want to use in your jServer

        keyManagerPassword: the keyManager password you want to use in your jServer

        vault: a folder name where your key files should be stored/retrieved.
        a submap with the keyStoreFileNameNoExtension will be created.[/I]
      • GetNotAfter As Long
        Can only be called right after the GenerateJKS() call.
        Is also saved in your vault location/{keystoreFileNameNoExtension} as 'range.times'.
      • GetNotBefore As Long
        Can only be called right after the GenerateJKS() call.
        Is also saved in your vault location/{keystoreFileNameNoExtension} as 'range.times'.
      • Initialize (wwwFolder As String)
        wwwFolder: needs to be the 'entry point' in your webserver (NOT your B4X app). In my case it was var/www/html and not var/www/)
        MUST be accessible on port 80. Is for example your Apache Server.
      • ReloadJKS (SslConfiguration As SslContextFactory)
        Reloads the SslConfiguration of the jServer

        Based on the reloadSSLConfiguration method written by OliverA
        More info: https://www.b4x.com/android/forum/t...-sslcontextfactory-reload.128051/#post-802925
    • Properties:
      • DomainChainFile As String [write only]
        File name of the signed certificate. Default domain-chain.crt
      • DomainCsrFile As String [write only]
        File name of the CSR. Default domain.csr
      • DomainKeyFile As String [write only]
        File name of the Domain Key Pair. Default domain.key

        If the file does not exist, a new key pair is generated and saved.
      • UserKeyFile As String [write only]
        File name of the User Key Pair. Default user.key

        If the file does not exist, a new key pair is generated and saved.

        Keep this key pair in a safe place! In a production environment, you will not be
        able to access your account again if you should lose the key pair.

Example usage (here for ABMServer Webapp, but similar for a normal jServer App or a BANanoServer Webapp).

Requires a server that is purely accessible on port 80. Is e.g. an Apache server.

B4X:
#Region  Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
#End Region

Sub Process_Globals
    Public Server As ABMServer 'requires 1.23+
    Public MyTheme As ABMTheme
    Public ABM As ABMaterial 'ignore

   ' JKS variables
    Public JKS As ABKeystoreSSL
    Public JKSReloadTimer As Timer

    Public JKSName As String = "keystore"
    Public JKSStorePassword As String = "01234"
    Public JKSManagerPassword As String = "56789"
End Sub

Sub AppStart (Args() As String)
    ' must be the first line in AppStart. DO NOT DELETE OR CHANGE!
    ' ------------------------------------------------------------
    ABM.SessionCacheControlV3 = "ABMCacheV3"
    ' ------------------------------------------------------------

    Dim DonatorKey As String = ""
    Server.Initialize("", DonatorKey, "template") ' Application = ' the handler that will be used in the url e.g. http://localhost:51042/template
    ' some parameters
    Server.Port = 51042
    Server.PortSSL = 51043

    ...

    ' create the pages
    Dim myPage As ABMPageTemplate
    myPage.Initialize    
    ' add the pages to the app
    Server.AddPage(myPage.page)
    ' do the same for your own pages: dim, initialize and Server.AddPage
    ' ...
 
    ' start the server
    If Server.PortSSL <> 0 Then
        ' make the jks file (if needed)
        MakeJKS
 
        If File.Exists(File.DirApp, JKSName & ".jks") Then
            Log("Starting server in HTTPS mode...")
            Server.StartServerHTTP2(JKSName & ".jks", JKSStorePassword, JKSManagerPassword)
 
            ' set a timer for a day
            JKSReloadTimer.Initialize("JKSReloadTimer", 24*60*60*1000)
            JKSReloadTimer.Enabled = True
        Else
            ' start it without https
            LogError("Failed to open or create " & JKSName & ".jks" & ". Starting server in HTTP mode...")
            Server.StartServer
        End If
    Else
        Server.StartServer
    End If

    ' redirect the output of the logs to a file if in release mode
    Server.RedirectOutput(File.DirApp, "logs.txt")
    
    StartMessageLoop
End Sub

' creates a certificate with Let's Encrypt
Sub MakeJKS() As Boolean
    Dim Result As Boolean
    ' needs to be the 'entry point' in your webserver (NOT your B4X app). In my case it was var/www/html and not var/www/)
    ' MUST be accessible on port 80
    JKS.Initialize("/var/www/html/")

    Dim domains As List
    domains.Initialize
    domains.Add("yourdomain.com")

    ' when developing, I check here if it is my local PC. If so I do not use the production server of Let's Encrypt
    If ABM.GetMyIP = "192.168.86.150" Then
        Result = JKS.GenerateJKS(domains, File.DirApp, JKSName, JKSStorePassword, JKSManagerPassword, False, "K:\KeyVault", False)
        If File.Exists(File.DirApp, JKSName & ".jks") = False Then
            ' let's try to make a self signed certificate for development
            LogError("Failed to open or create " & JKSName & ".jks with Let's Encrypt. Let's try to make a self signed one...")
            Result = JKS.GenerateSelfSignedJKS(File.DirApp, JKSName, JKSStorePassword, JKSManagerPassword, "K:\KeyVault")
        End If
    Else
        Result = JKS.GenerateJKS(domains, File.DirApp, JKSName, JKSStorePassword, JKSManagerPassword, True, "../KeyVault", False)
    End If

    Return Result
End Sub

Sub JKSReloadTimer_Tick
    If MakeJKS Then
        ' we created a new one, so reload it in the server
        JKS.ReloadJKS(Server.GetSSLConfiguration)
    End If
End Sub

Alwaysbusy
 
Last edited:

codie01

Active Member
Licensed User
Longtime User
Hi Alian and all,

So I have altered my main Module as shown below! When I compile I get the following error:

ABMApplication is declared twice. You should either remove the library reference or the code module.

I can not see it declared twice and I do not want to delete it as it forms my login screen and more!

B4X:
    'Non-UI application (console / server application)
#Region  Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    #AdditionalJar: mysql-connector-java-5.1.36-bin
    #AdditionalJar: mariadb-java-client-2.4.4.jar
    #AdditionalJar: javase-2.2
    '#AdditionalJar: itextpdf-5.5.6   
    #AdditionalJar: itextpdf-5.0.6
    #AdditionalJar:nextreports-engine-9.1
    #AdditionalJar:commons-jexl-2.1.1
    #AdditionalJar:commons-logging-1.1.3
    #AdditionalJar:itext-2.1.7
    #AdditionalJar:itext-rtf-2.1.7
    #AdditionalJar:xstream-1.3.1
    #AdditionalJar:xstream-1.4.7
    #AdditionalJar:poi-3.7
#End Region

Sub Process_Globals
    Public Server As ABMServer 'requires 1.23+
    'Public MyTheme As ABMTheme
    Public ABM As ABMaterial 'ignore

    ' JKS variables
    Public JKS As ABKeystoreSSL
    Public JKSReloadTimer As Timer

    Public JKSName As String = "keystore"
    Public JKSStorePassword As String = "01234"
    Public JKSManagerPassword As String = "56789"
End Sub

Sub AppStart (Args() As String)
    ' the user needs to login
    'ABMShared.NeedsAuthorization = True
   
        ' must be the first line in AppStart. DO NOT DELETE OR CHANGE!
    ' ------------------------------------------------------------
    ABM.SessionCacheControlV3 = "ABMCacheV3"
    ' ------------------------------------------------------------

    Dim DonatorKey As String = ""
    Server.Initialize("", DonatorKey, "rose") ' Application = ' the handler that will be used in the url e.g. http://localhost:51042/template
    ' some parameters
    Server.Port = xxxxx
    Server.PortSSL = xxxxx
   
    ' Build the Theme ------------------------------------
    ABMShared.BuildTheme("mytheme")
   
    ' create the app
'    Dim myApp As ABMApplication
'    myApp.Initialize
       
    ' create the pages
    Dim myDashboard As dashboard2Genero
    myDashboard.Initialize
    Dim mySales As salesGenero
    mySales.Initialize
    Dim myContracts As contractsGenero
    myContracts.Initialize
    Dim myItems As itemsGenero
    myItems.Initialize
    Dim myCategory As categoryGenero
    myCategory.Initialize
    Dim myFees As feesGenero
    myFees.Initialize
    Dim myStores As storesGenero
    myStores.Initialize
    Dim myTake As takinGenero
    myTake.Initialize
    Dim myTakeout As takeoutGenero
    myTakeout.Initialize
    Dim myReports As reportGenero
    myReports.Initialize
    Dim myUsers As usersGenero
    myUsers.Initialize
    Dim myControl As controlGenero
    myControl.Initialize
    Dim mySchedule As scheduleGenero
    mySchedule.Initialize
    Dim myFirst As firstGenero
    myFirst.Initialize
    Dim myMail As mailGenero
    myMail.Initialize
    Dim myFile As filemanagerGenero
    myFile.Initialize
   
    ' add the pages to the app
    Server.AddPage(myDashboard.Page)
    Server.AddPage(mySales.Page)
    Server.AddPage(myContracts.Page)
    Server.AddPage(myItems.Page)
    Server.AddPage(myCategory.Page)
    Server.AddPage(myFees.Page)
    Server.AddPage(myStores.Page)
    Server.AddPage(myTake.Page)
    Server.AddPage(myTakeout.Page)
    Server.AddPage(myReports.Page)
    Server.AddPage(myUsers.Page)
    Server.AddPage(myControl.Page)
    Server.AddPage(mySchedule.Page)
    Server.AddPage(myFirst.Page)
    Server.AddPage(myMail.Page)
    Server.AddPage(myFile.Page)
   
    '/startbackground workers ------------------------------------
    Server.AddBackgroundWorker("dashboardGenero")

    ' start the server
    If Server.PortSSL <> 0 Then
        ' make the jks file (if needed)
        MakeJKS
    
        If File.Exists(File.DirApp, JKSName & ".jks") Then
            Log("Starting server in HTTPS mode...")
            Server.StartServerHTTP2(JKSName & ".jks", JKSStorePassword, JKSManagerPassword)
    
            ' set a timer for a day
            JKSReloadTimer.Initialize("JKSReloadTimer", 24*60*60*1000)
            JKSReloadTimer.Enabled = True
        Else
            ' start it without https
            LogError("Failed to open or create " & JKSName & ".jks" & ". Starting server in HTTP mode...")
            Server.StartServer
        End If    
    Else
        Server.StartServer
    End If

    ' redirect the output of the logs to a file if in release mode
    Server.RedirectOutput(File.DirApp, "logs.txt")

    StartMessageLoop
End Sub

' creates a certificate with Let's Encrypt
Sub MakeJKS() As Boolean
    Dim Result As Boolean
    ' needs to be the 'entry point' in your webserver (NOT your B4X app). In my case it was var/www/html and not var/www/)
    ' MUST be accessible on port 80
    JKS.Initialize("/var/www/html/")

    Dim domains As List
    domains.Initialize
    domains.Add("shr-consignorconnect.com")

    ' when developing, I check here if it is my local PC. If so I do not use the production server of Let's Encrypt
    If ABM.GetMyIP = "192.168.1.65" Then
        Result = JKS.GenerateJKS(domains, JKSName, JKSStorePassword, JKSManagerPassword, False, "K:\KeyVault", False)
        If File.Exists(File.DirApp, JKSName & ".jks") = False Then
            ' let's try to make a self signed certificate for development
            LogError("Failed to open or create " & JKSName & ".jks with Let's Encrypt. Let's try to make a self signed one...")
            Result = JKS.GenerateSelfSignedJKS(JKSName, JKSStorePassword, JKSManagerPassword, "K:\KeyVault")
        End If
    Else
        Result = JKS.GenerateJKS(domains, JKSName, JKSStorePassword, JKSManagerPassword, True, "../KeyVault", False)
    End If

    Return Result
End Sub

Sub JKSReloadTimer_Tick
    If MakeJKS Then
        ' we created a new one, so reload it in the server
        JKS.ReloadJKS(Server.GetSSLConfiguration)
    End If
End Sub
 

alwaysbusy

Expert
Licensed User
Longtime User
it would be something like this:

In Process_Globals add:
B4X:
' JKS variables
Public JKS As ABKeystoreSSL
Public JKSReloadTimer As Timer

Public JKSName As String = "keystore"
Public JKSStorePassword As String = "01234"
Public JKSManagerPassword As String = "56789"

Around your myApp.StartServerHTTP2(...
B4X:
' make the jks file (if needed)
MakeJKS
    
If File.Exists(File.DirApp, JKSName & ".jks") Then
    myApp.StartServerHTTP2(srvr, "srvr", Port, PortSSL, JKSName & ".jks", JKSStorePassword, JKSManagerPassword)
Else
    myApp.StartServer(srvr, "srvr", Port)
end if

And add these two methods (changed to your Apache settings of course)
B4X:
' creates a certificate with Let's Encrypt
Sub MakeJKS() As Boolean
    Dim Result As Boolean
    ' needs to be the 'entry point' in your webserver (NOT your B4X app). In my case it was var/www/html and not var/www/)
    ' MUST be accessible on port 80
    JKS.Initialize("/var/www/html/")
 
    Dim domains As List
    domains.Initialize
    domains.Add("yourdomain.com")
 
    ' when developing, I check here if it is my local PC. If so I do not use the production server of Let's Encrypt
    If ABM.GetMyIP = "192.168.86.150" Then
        Result = JKS.GenerateJKS(domains, JKSName, JKSStorePassword, JKSManagerPassword, False, "K:\KeyVault", False)
        If File.Exists(File.DirApp, JKSName & ".jks") = False Then
            ' let's try to make a self signed certificate for development
            LogError("Failed to open or create " & JKSName & ".jks with Let's Encrypt. Let's try to make a self signed one...")
            Result = JKS.GenerateSelfSignedJKS(JKSName, JKSStorePassword, JKSManagerPassword, "K:\KeyVault")
        End If
    Else
        Result = JKS.GenerateJKS(domains, JKSName, JKSStorePassword, JKSManagerPassword, True, "../KeyVault", False)
    End If
 
    Return Result
End Sub

Sub JKSReloadTimer_Tick
    If MakeJKS Then
        ' we created a new one, so reload it in the server
        JKS.ReloadJKS(Server.GetSSLConfiguration)
    End If 
End Sub

Alwaysbusy
 

codie01

Active Member
Licensed User
Longtime User
Thanks Alian,

Below is my AppStart code:

B4X:
Sub AppStart (Args() As String)
    ' the user needs to login
    'ABMShared.NeedsAuthorization = True
    
    ' Build the Theme ------------------------------------
    ABMShared.BuildTheme("mytheme")
    
    ' create the app
    Dim myApp As ABMApplication
    myApp.Initialize
        
    ' create the pages
    Dim myDashboard As dashboard2Genero
    myDashboard.Initialize
    Dim mySales As salesGenero
    mySales.Initialize
    Dim myContracts As contractsGenero
    myContracts.Initialize
    Dim myItems As itemsGenero
    myItems.Initialize
    Dim myCategory As categoryGenero
    myCategory.Initialize
    Dim myFees As feesGenero
    myFees.Initialize
    Dim myStores As storesGenero
    myStores.Initialize
    Dim myTake As takinGenero
    myTake.Initialize
    Dim myTakeout As takeoutGenero
    myTakeout.Initialize
    Dim myReports As reportGenero
    myReports.Initialize
    Dim myUsers As usersGenero
    myUsers.Initialize
    Dim myControl As controlGenero
    myControl.Initialize
    Dim mySchedule As scheduleGenero
    mySchedule.Initialize
    Dim myFirst As firstGenero
    myFirst.Initialize
    Dim myMail As mailGenero
    myMail.Initialize
    Dim myFile As filemanagerGenero
    myFile.Initialize
    
    ' add the pages to the app
    myApp.AddPage(myDashboard.Page)
    myApp.AddPage(mySales.Page)
    myApp.AddPage(myContracts.Page)
    myApp.AddPage(myItems.Page)
    myApp.AddPage(myCategory.Page)
    myApp.AddPage(myFees.Page)
    myApp.AddPage(myStores.Page)
    myApp.AddPage(myTake.Page)
    myApp.AddPage(myTakeout.Page)
    myApp.AddPage(myReports.Page)
    myApp.AddPage(myUsers.Page)
    myApp.AddPage(myControl.Page)
    myApp.AddPage(mySchedule.Page)
    myApp.AddPage(myFirst.Page)
    myApp.AddPage(myMail.Page)
    myApp.AddPage(myFile.Page)
    
    '/startbackground workers ------------------------------------
    srvr.AddBackgroundWorker("dashboardGenero")
    
    myApp.StartServer(srvr, "srvr", 51042)

    ' start server HTTP/2
    'myApp.StartServerHTTP2(srvr, "srvr", 51042, 51043, "keystore", "xxxxxxx", "xxxxxxx")
    
    StartMessageLoop
End Sub
 

alwaysbusy

Expert
Licensed User
Longtime User
Well, I would add those Global variables, then change your code to:

B4X:
Sub AppStart (Args() As String)
    ' the user needs to login
    'ABMShared.NeedsAuthorization = True
    
    ' Build the Theme ------------------------------------
    ABMShared.BuildTheme("mytheme")
    
    ' create the app
    Dim myApp As ABMApplication
    myApp.Initialize
        
    ' create the pages
    Dim myDashboard As dashboard2Genero
    myDashboard.Initialize
    Dim mySales As salesGenero
    mySales.Initialize
    Dim myContracts As contractsGenero
    myContracts.Initialize
    Dim myItems As itemsGenero
    myItems.Initialize
    Dim myCategory As categoryGenero
    myCategory.Initialize
    Dim myFees As feesGenero
    myFees.Initialize
    Dim myStores As storesGenero
    myStores.Initialize
    Dim myTake As takinGenero
    myTake.Initialize
    Dim myTakeout As takeoutGenero
    myTakeout.Initialize
    Dim myReports As reportGenero
    myReports.Initialize
    Dim myUsers As usersGenero
    myUsers.Initialize
    Dim myControl As controlGenero
    myControl.Initialize
    Dim mySchedule As scheduleGenero
    mySchedule.Initialize
    Dim myFirst As firstGenero
    myFirst.Initialize
    Dim myMail As mailGenero
    myMail.Initialize
    Dim myFile As filemanagerGenero
    myFile.Initialize
    
    ' add the pages to the app
    myApp.AddPage(myDashboard.Page)
    myApp.AddPage(mySales.Page)
    myApp.AddPage(myContracts.Page)
    myApp.AddPage(myItems.Page)
    myApp.AddPage(myCategory.Page)
    myApp.AddPage(myFees.Page)
    myApp.AddPage(myStores.Page)
    myApp.AddPage(myTake.Page)
    myApp.AddPage(myTakeout.Page)
    myApp.AddPage(myReports.Page)
    myApp.AddPage(myUsers.Page)
    myApp.AddPage(myControl.Page)
    myApp.AddPage(mySchedule.Page)
    myApp.AddPage(myFirst.Page)
    myApp.AddPage(myMail.Page)
    myApp.AddPage(myFile.Page)
    
    '/startbackground workers ------------------------------------
    srvr.AddBackgroundWorker("dashboardGenero")

    ' NEW------------------------------------------------------------------------------
    ' make the jks file (if needed)
    MakeJKS
    
    If File.Exists(File.DirApp, JKSName & ".jks") Then
        myApp.StartServerHTTP2(srvr, "srvr", 51042, 51043, JKSName & ".jks", JKSStorePassword, JKSManagerPassword)
    Else
        myApp.StartServer(srvr, "srvr", 51042)
    end if
    ' END NEW------------------------------------------------------------------------------
    StartMessageLoop
End Sub

And add the MakeJKS (changed to your environment) and the JKSReloadTimer_Tick code.

Alwaysbusy
 

codie01

Active Member
Licensed User
Longtime User
Thanks Again Alian,

You continue to give amazing support. Donate time again I think for your time.

I have one last issue in the sub JKSReloadTimer_Tick the code JKS.ReloadJKS(srvr.GetSSLConfiguration) throws and exception as it is a sub of the library ABMServer. When I select this library for inclusion the fault goes away. However that then immediately throws the error "ABMApplication is declared twice. You should either remove the library reference or the code module"

Is there another way to reload or get the srvr SSL configuration?

B4X:
Sub JKSReloadTimer_Tick
    If MakeJKS Then
        ' we created a new one, so reload it in the server
        JKS.ReloadJKS(srvr.GetSSLConfiguration)
    End If
End Sub
 

OliverA

Expert
Licensed User
Longtime User
vault: a folder name where your key files should be stored/retrieved.

Issue report:
I'm currently testing this library in a staging environment. After a successful running GenerateJKS for the first time, I noticed the following:
These files were created in the vault path:
domain.csr
domain.key
range.times
user.key
These files were created in File.DirApp:
domain-chain.crt
domain.csr
domain.key
keystore.jks
user.key
I would have expected the new keystore.jks file in the File.DirApp, but not the other files. I would have thought all of those would be in the vault path.
Notes:
1) The following command (slightly edited) was used:
B4X:
JKS.GenerateJKS(domains, "keystore", "xxx", "xxx", False, File.Combine("/etc", File.Combine("ACMEv2", "xyz.notreal.com")), False)
2) Deleting the extra files in File.DirApp (but not the .jks) and re-running the command above does not recreate them (if the cert is still valid)
3) Setting ForceGenerate to True will re-create the files in File.DirApp

Feature request:
Would it be possible in a future edition to be able to tell the library where to create the keystore file? In some environments, multiple server processes on the same host could use the same keystore file for encryption purposes.
 

alwaysbusy

Expert
Licensed User
Longtime User
I would have expected the new keystore.jks file in the File.DirApp, but not the other files
I purposely did not delete them after moving them to the vault (because, especially user.key is so important). I thought, after doing a check to be sure, one could delete them themselves. I look into the internals of the lib I used if I can immediately create them in the vault.

EDIT: just noticed in your post the domain-chain.crt is not moved. I'll have to check that...

Would it be possible in a future edition to be able to tell the library where to create the keystore file? In some environments, multiple server processes on the same host could use the same keystore file for encryption purposes.
Good idea. I will make the change.

Alwaysbusy
 

alwaysbusy

Expert
Licensed User
Longtime User

OliverA

Expert
Licensed User
Longtime User
Getting some more time to test this. I've updated the lib to 1.07 and now I get a
Vault can not be empty!
error. This is for testing new staging certificates, and therefore should be a given that the vault is empty (since your lib has not created the keys for me yet to fetch a staging certificate).
 

OliverA

Expert
Licensed User
Longtime User
No, the vault directory is set and the vault directory exists (I test for its existence). I can pm you the exact call to GenerateJKS if you need it. Here's the B4J log:
 

OliverA

Expert
Licensed User
Longtime User
Can you test this one
Done. Works! Thank you.

Onward to testing renewing an existing certificate. Issue I seem to have is that I do not seem to have a domain.key file and a user.key file. The certificate was previously managed with certbot. I do have a privkey.pem file.
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…