admin管理员组文章数量:1316399
I have a class EncryptionManager
that generates public and private keys along with a x509 certificate. These are stored inside a Java keystore.
How would I go about programatically importing this certificate into the computers keychain?
I know the commands to run in a terminal, I just wondered if there is a way to do it from Java without needing to spawn a process that runs bash?
EDIT:
Someone pointed out this might be a duplicate. Let me expand a little.
My KeyStore
is also storing other keys and certificates for secure communication with other clients, not just SSL. The other answer tells me how to point the system at my keystore, but not how I would specify which key/cert to use.
Also, does the other answer work for all systems? On a Mac, the cert must be added to the System.keychain
using sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/.localhost-ssl/localhost.crt
, which I would like to do without spawning a bash process if possible.
I have a class EncryptionManager
that generates public and private keys along with a x509 certificate. These are stored inside a Java keystore.
How would I go about programatically importing this certificate into the computers keychain?
I know the commands to run in a terminal, I just wondered if there is a way to do it from Java without needing to spawn a process that runs bash?
EDIT:
Someone pointed out this might be a duplicate. Let me expand a little.
My KeyStore
is also storing other keys and certificates for secure communication with other clients, not just SSL. The other answer tells me how to point the system at my keystore, but not how I would specify which key/cert to use.
Also, does the other answer work for all systems? On a Mac, the cert must be added to the System.keychain
using sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/.localhost-ssl/localhost.crt
, which I would like to do without spawning a bash process if possible.
2 Answers
Reset to default 2I tried couple of different keychains but somehow it is restricted for the following keychain files:
/Library/Keychains/System.keychain
/System/Library/Keychains/SystemRootCertificates.keychain
The only one which I was able to append additional certificates was to the following keychain file:
/Users/[MY_USER]/Library/Keychains/login.keychain-db
The following code should do the trick and additional it will print the console output if you have any errors:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder(
"security", "add-trusted-cert",
"-d", "-r", "trustAsRoot",
"-k", "/Users/[MY_USER]/Library/Keychains/login.keychain-db",
"/absolute-path/to/my-custom-root-ca.crt");
Process process = processBuilder.start();
String consoleOutput = getConsoleOutput(process);
System.out.println(consoleOutput);
}
private static String getConsoleOutput(Process process) throws IOException {
try (InputStream inputStream = process.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
return bufferedReader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
}
}
You don't need to include the sudo command, but you will be prompted to scan either your finger or enter the password. I am not sure how to avoid that part yet, but my assumption is that it is required.
Another sidenote to this solution is that a path to a binary certificate is required. So if you use a keystore file either in memory or on the filesystem you first need to extract the certificates from it and save it on the filesystem. Get the absolute file paths for all of those files and loop through it for every file with the mac command in the code example.
Whilst the above answer is still the accepted answer (Thanks @Hakan54), I would like to provide my full solution for anyone else who comes across this.
I know the question was originally Java, but my application is Scala and the answer is easy enough to translate to Java. I have been trying to auto-configure ssl with generated certificates entirely within my application for use with SSLContext
and nginx as part of my question here: https://serverfault/questions/1171697/how-is-best-to-implement-ssl-for-my-setup
The full solution generates self-signed certificates and keys into a Java keystore, loads them into macos keychain and generates nginx
config using the certificate and key for ssl.
The solution:
KeyStoreManagement
class:
package utils
import .bouncycastle.asn1.x500.X500Name
import .bouncycastle.cert.jcajce.{JcaX509CertificateConverter, JcaX509v3CertificateBuilder}
import .bouncycastle.jce.provider.BouncyCastleProvider
import .bouncycastle.operator.jcajce.JcaContentSignerBuilder
import java.io.{File, FileInputStream, FileOutputStream}
import java.math.BigInteger
import java.security.*
import java.security.cert.Certificate
import java.security.spec.{ECGenParameterSpec, X509EncodedKeySpec}
import java.util.{Base64, Calendar, Date}
trait KeyStoreManagement {
def createCert(keyPair: KeyPair, keyAlgorithm: String, domain: String): Certificate = {
val dnName = new X500Name("CN=" + domain)
val serialNumber = BigInteger.ONE
val startDate = new Date()
val calendar = Calendar.getInstance()
calendar.setTime(startDate)
calendar.add(Calendar.YEAR, 100)
val endDate = calendar.getTime
val contentSigner = new JcaContentSignerBuilder("SHA256with" + keyAlgorithm).build(keyPair.getPrivate)
val certificateBuilder = new JcaX509v3CertificateBuilder(dnName, serialNumber, startDate, endDate, dnName, keyPair.getPublic)
JcaX509CertificateConverter().setProvider(new BouncyCastleProvider).getCertificate(certificateBuilder.build(contentSigner))
}
def loadKeyStore(keyStorePath: String, keyStorePass: Array[Char]): KeyStore = {
val keyStore = KeyStore.getInstance("PKCS12")
val keyFile = new File(keyStorePath)
if (keyFile.exists) {
keyStore.load(new FileInputStream(keyFile), keyStorePass)
} else {
keyStore.load(null, null)
keyStore.store(new FileOutputStream(keyFile), keyStorePass)
}
keyStore
}
def getCertificateString(keyStorePath: String, keyStorePass: Array[Char], alias: String): String = {
val keyStore = loadKeyStore(keyStorePath, keyStorePass)
val certificate = keyStore.getCertificate(alias)
Base64.getEncoder.encodeToString(certificate.getEncoded)
}
def createNewCert(keyStore: KeyStore, keyAlgorithm: String, domain: String, alias: String, keyPass: Array[Char], keyStorePath: String, keyStorePass: Array[Char]): Unit = {
val keyPair = generateKey(keyAlgorithm)
val cert = createCert(keyPair, keyAlgorithm, domain)
val chain = Array[Certificate](cert)
keyStore.setKeyEntry(alias, keyPair.getPrivate, keyPass, chain)
val keyFile = new File(keyStorePath)
keyStore.store(new FileOutputStream(keyFile), keyStorePass)
}
def toPemFormat(certificate: Certificate): String = {
"-----BEGIN CERTIFICATE-----\n" + Base64.getEncoder.encodeToString(certificate.getEncoded) + "\n-----END CERTIFICATE-----"
}
def toPemFormat(privateKey: PrivateKey): String = {
"-----BEGIN PRIVATE KEY-----\n" + Base64.getEncoder.encodeToString(privateKey.getEncoded) + "\n-----END PRIVATE KEY-----"
}
def toPemCombined(certificate: Certificate, privateKey: PrivateKey): String = {
toPemFormat(privateKey) + "\n" + toPemFormat(certificate)
}
def generateKey(keyAlgorithm: String): KeyPair = {
keyAlgorithm match {
case "ECDSA" =>
val keyPairGenerator = KeyPairGenerator.getInstance("EC", "SunEC")
val ecParameterSpec = new ECGenParameterSpec("secp256r1")
keyPairGenerator.initialize(ecParameterSpec)
keyPairGenerator.genKeyPair()
case "RSA" =>
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
keyPairGenerator.initialize(2048)
keyPairGenerator.genKeyPair()
}
}
}
SSLManager
class:
package server
import database.DatabaseUtil
import server.SSLManager._
import server.WebServer.SERVER_ID
import utils.OS.OSVersion.*
import utils.{KeyStoreManagement, OS}
import java.io.File
import java.security.*
import javax.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}
import scala.language.postfixOps
import scala.sys.process.stringSeqToProcess
object SSLManager {
private val SSL_ROOT: String = System.getProperty("user.home") + File.separator + ".localhost-ssl" + File.separator
val KEY_STORE_PATH: String = SSL_ROOT + "keystore_name.pkcs12"
val SSL_CERTIFICATE_PATH: String = SSL_ROOT + "localhost.crt"
val SSL_KEY_PATH: String = SSL_ROOT + "localhost.key"
}
/**
* Class for generating SSL Certificates into a Java KeyStore and adding them to the system keychain
*/
class SSLManager extends KeyStoreManagement with FileHandler {
private val KEY_PASS = DatabaseUtil.hashString("key_pass").toCharArray
private val KEY_STORE_PASS = DatabaseUtil.hashString("key_store_pass").toCharArray
private val ALIAS = "sslCertificateAlias"
private var sslContext: SSLContext = _
def init(): Unit = {
if (!fileExists(KEY_STORE_PATH)) {
mkDirs(SSL_ROOT)
// generate RSA key pair
val keyPair = generateKey("RSA")
// generate certificate
val certificate = createCert(keyPair, "RSA", "localhost")
val keyPem = toPemFormat(keyPair.getPrivate)
val certPem = toPemFormat(certificate)
writeFile(SSL_CERTIFICATE_PATH, Seq(certPem))
writeFile(SSL_KEY_PATH, Seq(keyPem))
// load the key into keystore and create
val command = s"openssl pkcs12 -export -out $KEY_STORE_PATH -in ${SSL_ROOT + "localhost.crt"} -inkey ${SSL_ROOT + "localhost.key"} -passout pass:"
+ DatabaseUtil.hashString(SERVER_ID)
command.split(" ").toSeq !!
// register certificate with OS
registerWithOS()
}
val keyStore = loadKeyStore(KEY_STORE_PATH, KEY_STORE_PASS)
val entry = keyStore.getEntry(ALIAS, new KeyStore.PasswordProtection(KEY_PASS))
val cert = keyStore.getCertificate(ALIAS)
val privateKeyEntry = classOf[KeyStore.PrivateKeyEntry].cast(entry)
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(keyStore, KEY_STORE_PASS)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(keyStore)
sslContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
}
private def registerWithOS(): Unit = {
if (OS.getOS == MAC) {
val command = s"security add-trusted-cert -d -r trustRoot -k " +
s"${System.getProperty("user.home")}/Library/Keychains/login.keychain-db ${SSL_ROOT + "localhost.crt"}"
command.split(" ").toSeq!!
} else if (OS.getOS == WINDOWS) {
//TODO
} else {
/TODO
}
}
def getSSLContext: SSLContext = sslContext
}
Nginx
class:
package utils
import server.FileHandler
import server.SSLManager.*
import utils.OS.OSVersion.*
import java.io.File
import scala.language.postfixOps
import scala.sys.process.stringSeqToProcess
object Nginx extends FileHandler {
private val UI_ROOT: String = System.getProperty("user.home") + File.separator + "ui"
def init(): Unit = {
OS.getOS match {
case MAC =>
if (!fileExists("/usr/local/etc/nginx")) {
throw new RuntimeException("nginx is not installed. Please install nginx before launching.")
} else if (!fileExists("/usr/local/etc/nginx/servers/default.conf")) {
// write config
val config = readResource("nginx/default")
.replace("$SSL_CERTIFICATE", SSL_CERTIFICATE_PATH)
.replace("$SSL_KEY", SSL_KEY_PATH)
.replace("$ROOT", UI_ROOT)
writeFile("/usr/local/etc/nginx/servers/hydra.conf", Seq(config))
// restart nginx
Seq("brew", "services", "restart", "nginx")!!
}
case LINUX =>
//TODO
case WINDOWS =>
//TODO
}
}
}
Then to use:
val sslManager = new SSLManager()
sslManager.init()
val sslContext = sslManager.getSSLContext
val https: HttpsConnectionContext = ConnectionContext.httpsServer(sslContext)
// setup nginx
Nginx.init()
// create https Engine
ConnectionContext.httpsServer(() => {
val engine = sslContext.createSSLEngine()
engine.setUseClientMode(false)
engine.setNeedClientAuth(true)
engine
})
Http().newServerAt("localhost", 8080).enableHttps(https).bind(routes.routes)
.onComplete {
case Success(binding) =>
val address = binding.localAddress
SYSTEM.log.info(s"Server is listening on ${address.getHostString}:${address.getPort}")
case Failure(ex) =>
SYSTEM.log.error("Server could not be started", ex)
stop()
}
}
default
nginx config (obviously the routes are specific to my application):
server {
server_name www.localhost;
ssl_certificate $SSL_CERTIFICATE;
ssl_certificate_key $SSL_KEY;
index index.html index.htm;
root $ROOT;
location / {
try_files $uri.html $uri/index.html
@public
@nextjs;
add_header Cache-Control "public, max-age=3600";
}
location @public {
add_header Cache-Control "public, max-age=3600";
}
location /ping {
proxy_pass https://localhost:8080;
}
location /auth {
proxy_pass https://localhost:8080;
}
location @nextjs {
proxy_pass https://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
}
本文标签:
版权声明:本文标题:macos - How to add self-signed generated certificate to trusted certificates from inside a Java keystore? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742002470a2411291.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
/usr/local/share/ca-certificates
however the format yould be PEM – Hakan54 Commented Jan 29 at 10:56