Set Up a Corporate SSL Certificate¶
This guide shows how to configure SSL certificates in environments with an SSL Inspection (MITM) proxy, covering Java applications (Wizard, BotRunner, BotStudio) and Python (requests).
Context¶
Corporate environments with SSL Inspection (MITM proxy) intercept HTTPS connections and replace the server's original certificate with one issued by an internal company CA. This causes Java and Python applications to reject the connection, since they don't recognize that CA.
This behavior usually results in SSL errors when using tools like the BotCity Runner, typically something like PKIX path building failed or unable to find valid certification path to requested target.
One possible solution is to register the internal CA's root certificate in each technology's respective truststore.
Prerequisites¶
-
The company's internal CA root certificate (
.crt,.cer, or.pemfile), provided by the infrastructure/security team.If you need to extract it, you can use the browser: access the target server from the corporate machine, click the lock icon, export the certificate chain, and identify the root certificate (the one at the top of the chain, which is self-signed).
-
The Java (JDK/JRE) used to run the applications.
Here you need to know which Java is being used to run the tools (Runner): the Java bundled with the SDK installation, or the Java from the system installation.
-
Python with the
certifipackage installed (pip install --upgrade certifi).
Configuration for Java¶
1. Locate the JVM's cacerts¶
cacerts is Java's default truststore. Its location depends on the version and the Java being used:
# cacerts on Java 9+
# $JAVA_HOME\lib\security\cacerts
# cacerts on Java 8
# $JAVA_HOME\jre\lib\security\cacerts
# Command that can help identify the installed Java
# java -version
If the Java used to run the tools is the one bundled with the BotCity Studio SDK, the path to cacerts should look like:
Confirm the file exists:
2. List the existing certificates (optional)¶
keytool is a utility that already ships with the JDK, located at <PATH_JAVA>\bin\keytool.exe. This is the tool used for all certificate operations.
# To make the calls easier, open a terminal in the "<PATH_JAVA>\bin" directory
.\keytool.exe -list -keystore "<PATH_JAVA>\lib\security\cacerts" -storepass changeit
This command should return the list of existing default certificates.
3. Import the certificate¶
With the company's certificate file in hand, use keytool to import it into the cacerts file. You can also set an alias to make it easier to identify the imported certificate later.
.\keytool.exe -importcert -alias company-internal-ca -file "C:\certs\company-root-ca.crt" -keystore "<PATH_JAVA>\lib\security\cacerts" -storepass changeit -noprompt
Note
The default cacerts password is changeit. Use a descriptive alias to make future identification easier. If there are intermediate certificates, import each one with a different alias.
4. Verify the import¶
.\keytool.exe -list -keystore "<PATH_JAVA>\lib\security\cacerts" -storepass changeit -alias company-internal-ca
The output should show the alias, the creation date, and the certificate type.
5. Restart the Java application¶
The JVM loads the truststore at startup, so the process needs to be restarted for the new certificate to be recognized.
Configuration for Python (requests / certifi)¶
The requests library doesn't use the operating system's truststore or Java's cacerts. It relies on the certifi package, which ships its own bundle of trusted CAs (a cacert.pem file).
1. Locate the certifi bundle¶
Note the path returned (for example, C:\Users\user\AppData\...\certifi\cacert.pem).
2. Create the custom bundle¶
Copy the original bundle and add the internal CA's certificate to the end:
# Creates the destination directory (if it doesn't exist)
New-Item -ItemType Directory -Path "C:\Users\<user>\Documents\Certs" -Force
# Copies the original certifi bundle
Copy-Item -Path (python -c "import certifi; print(certifi.where())") -Destination "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem" -Force
# Adds the internal CA's root certificate to the end of the bundle
Add-Content -Path "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem" -Value ""
Get-Content -Path "C:\certs\company-root-ca.crt" | Add-Content -Path "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem"
3. Set the environment variable¶
Option A: for the current session only (quick test)
Option B: persistent for the current user
[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem", "User")
Option C: persistent for the whole machine (requires PowerShell as Admin)
[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem", "Machine")
Tip
If other scripts or libraries besides requests also need to trust the internal CA (for example, httpx, urllib3), also set the SSL_CERT_FILE variable to the same path, following the same procedure above.
4. Verify the configuration¶
Open a new terminal (so the persistent variable gets loaded) and test:
# Confirms the variable is set
echo $env:REQUESTS_CA_BUNDLE
# Tests the connection
python -c "import requests; r = requests.get('https://developers.botcity.dev/api/v2/maestro/version'); print(r.status_code)"
5. Virtual environments¶
The REQUESTS_CA_BUNDLE variable overrides requests' default behavior of consulting the certifi package installed in the active venv. With the variable set, requests ignores certifi and uses the pointed-to file directly, so it works for any venv with no extra per-environment configuration needed.
Maintenance¶
Updating certifi¶
When the certifi package is updated via pip, the custom bundle is not updated automatically (it's a static copy). To rebuild it, just repeat steps 2 and 3 of the previous section.
Removing the Java certificate (if needed)¶
keytool -delete -keystore "<JAVA_PATH>\lib\security\cacerts" -storepass changeit -alias company-internal-ca
Removing the Python environment variables (if needed)¶
# Removes it from the user scope
[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", $null, "User")
# Or from the machine scope (requires Admin)
[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", $null, "Machine")
Troubleshooting¶
Java: PKIX path building failed / unable to find valid certification path
- Check that
JAVA_HOMEpoints to the correct JVM the application uses. - Confirm the imported certificate is the root one (not an intermediate).
- Check whether the application uses a custom truststore via
-Djavax.net.ssl.trustStore. If so, import the certificate into that truststore instead of the globalcacerts.
Python: SSLCertVerifyError / CERTIFICATE_VERIFY_FAILED
- Confirm the
REQUESTS_CA_BUNDLEvariable is set in the terminal where the script runs (echo $env:REQUESTS_CA_BUNDLE). - Check that the
.pemfile contains bothcertifi's CAs and the internal CA's certificate. - If the error persists, test with an explicit
verifyargument to isolate the problem:
import requests
r = requests.get("https://your-server.com", verify=r"C:\certs\company-ca-bundle.pem")
print(r.status_code)
Summary¶
| Step | Java | Python |
|---|---|---|
| Truststore | cacerts (JKS) |
certifi (PEM) |
| Tool | keytool |
Copy + PEM concatenation |
| Import command | keytool -importcert ... |
Copy-Item + Add-Content |
| Environment variable | javax.net.ssl.trustStore (if using a custom truststore) |
REQUESTS_CA_BUNDLE or SSL_CERT_FILE |
| Restart required | Yes (JVM) | New terminal |
Note
Replace the example paths (such as C:\certs\...) and the certificate alias with the real values from the customer's environment.