scala ssl认证
scala ssl认证
mqtt pom
<dependency>
<groupId>org.fusesource.mqtt-client</groupId>
<artifactId>mqtt-client</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version>
</dependency>
<!-- paly json : 解析json -->
<!-- https://mvnrepository.com/artifact/com.typesafe.play/play-json -->
<dependency>
<groupId>com.typesafe.play</groupId>
<artifactId>play-json_2.12</artifactId>
<version>2.9.2</version>
</dependency>
<!-- 1.47 1.55 SSL 认证 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.55</version>
</dependency>
mqtt code
package com.hs.fast.utils.mqtt
import java.io.{File, InputStream, InputStreamReader}
import java.nio.file.Files
import java.security.cert.{Certificate, CertificateFactory, X509Certificate}
import java.security.{KeyFactory, KeyPair, KeyStore, PrivateKey, Security}
import java.security.spec.{KeySpec, PKCS8EncodedKeySpec}
import javax.net.ssl.{KeyManagerFactory, SSLContext, SSLSocketFactory, TrustManagerFactory}
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openssl.{PEMKeyPair, PEMParser}
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo
/**
* @Title:
* @BelongProjecet hs_flink_elephant
* @BelongPackage com.hs.fast.utils.mqtt
* @Description:
* @Copyright time company - Powered By 研发一部
* @Author: cw
* @Date: 2023-11-3 17:11
* @Version V1.0
*/
object MySslUtil {
/**
这段代码是一个Scala函数,用于从输入流中加载私钥。它使用了Bouncy Castle库,这是一个开源的Java库,提供了一系列的加密和解密算法。
函数的主要步骤如下:
1. 定义一个PrivateKey变量`key`,初始值为null。
2. 检查是否已经安装了Bouncy Castle提供者(provider)。如果没有,则添加Bouncy Castle提供者。
3. 使用PEMParser从输入流中读取PEM格式的密钥对象。
4. 检查读取的密钥对象的类型。如果是PEMKeyPair(即,包含公钥和私钥的键对),则使用JcaPEMKeyConverter将其转换为Java的KeyPair对象,并获取私钥。
5. 如果读取的密钥对象不是PEMKeyPair,但是PKCS8EncryptedPrivateKeyInfo或PrivateKeyInfo类型(即,PKCS#8格式的私钥),则获取其编码的字节数组。
6. 如果获取到了编码的字节数组,使用KeyFactory生成RSA私钥。
7. 返回生成的私钥。
这个函数可以处理PEM格式和PKCS#8格式的私钥,这两种格式都是常见的私钥格式。如果输入流中的私钥不是这两种格式,或者无法正确解析,函数将返回null。
*/
@throws[Exception]
def loadKey(keyFile: InputStream): PrivateKey = {
var key: PrivateKey = null
val bcProvider = "BC"
if (Security.getProvider(bcProvider) == null) Security.addProvider(new BouncyCastleProvider)
val pemParser = new PEMParser(new InputStreamReader(keyFile))
val keyObj = pemParser.readObject
if (keyObj.isInstanceOf[PEMKeyPair]) {
val converter = new JcaPEMKeyConverter().setProvider(bcProvider)
val keyPair = converter.getKeyPair(keyObj.asInstanceOf[PEMKeyPair])
key = keyPair.getPrivate
}
else {
var keyEncoded:Array[Byte] = null
if (keyObj.isInstanceOf[PKCS8EncryptedPrivateKeyInfo]) keyEncoded = keyObj.asInstanceOf[PKCS8EncryptedPrivateKeyInfo].getEncoded
else if (keyObj.isInstanceOf[PrivateKeyInfo]) keyEncoded = keyObj.asInstanceOf[PrivateKeyInfo].getEncoded
if (keyEncoded != null) {
val keyFactory = KeyFactory.getInstance("RSA")
val keySpec = new PKCS8EncodedKeySpec(keyEncoded)
key = keyFactory.generatePrivate(keySpec)
}
}
key
}
/*
这段代码是一个Scala函数,用于创建一个SSLSocketFactory,该工厂可以生成SSL套接字,用于安全的网络通信。这个函数需要三个参数:CA证书的路径,客户端证书的路径,以及客户端私钥的路径。
以下是函数的主要步骤:
1. 首先,函数打开了三个文件的输入流,分别对应CA证书,客户端证书,以及客户端私钥。
2. 然后,函数使用CertificateFactory生成X.509格式的证书对象。X.509是一种常见的证书格式,用于PKI(Public Key Infrastructure)系统。
3. 函数将CA证书加载到KeyStore中。KeyStore是Java的一个类,用于存储密钥和证书。
4. 函数使用TrustManagerFactory初始化信任管理器。信任管理器用于决定是否接受远程服务器的证书。
5. 函数将客户端证书和私钥加载到另一个KeyStore中。
6. 函数使用KeyManagerFactory初始化密钥管理器。密钥管理器用于决定使用哪个密钥进行身份验证。
7. 函数创建一个SSLContext对象,并使用密钥管理器和信任管理器进行初始化。SSLContext是Java的一个类,用于实现SSL(Secure Sockets Layer)协议。
8. 最后,函数关闭了所有打开的输入流,并返回SSLSocketFactory对象。
这个函数可以用于创建一个安全的网络连接,例如HTTPS连接。在创建连接时,会使用提供的证书和私钥进行身份验证,以确保通信的安全性。
*/
@throws[Exception]
def getSocketFactory(caCertPath: String, clientCertPath: String, clientKeyPath: String): SSLSocketFactory = {
var inputStreamCaCertPath: InputStream = null
var inputStreamCertPath: InputStream = null
var inputStreamKeyPath: InputStream = null
inputStreamCaCertPath = Files.newInputStream(new File(caCertPath).toPath)
inputStreamCertPath = Files.newInputStream(new File(clientCertPath).toPath)
inputStreamKeyPath = Files.newInputStream(new File(clientKeyPath).toPath)
val certFactory = CertificateFactory.getInstance("X.509")
// load CA certificate into keystore to authenticate server
val caCert = certFactory.generateCertificate(inputStreamCaCertPath)
val x509CaCert = caCert.asInstanceOf[X509Certificate]
val caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType)
caKeyStore.load(null, null)
caKeyStore.setCertificateEntry("cacert", x509CaCert)
val tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
tmFactory.init(caKeyStore)
// load client certificate and private key to be used by server to authenticate client
val clientCert = certFactory.generateCertificate(inputStreamCertPath)
val x509ClientCert = clientCert.asInstanceOf[X509Certificate]
val password = "keypass"
val clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType)
clientKeyStore.load(null, null)
clientKeyStore.setCertificateEntry("cert", x509ClientCert)
clientKeyStore.setKeyEntry("key", loadKey(inputStreamKeyPath), password.toCharArray, Array[Certificate](x509ClientCert))
val kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
kmFactory.init(clientKeyStore, password.toCharArray)
val sslContext = SSLContext.getInstance("TLSv1.2")
sslContext.init(kmFactory.getKeyManagers, tmFactory.getTrustManagers, null)
inputStreamKeyPath.close()
inputStreamCertPath.close()
inputStreamKeyPath.close()
sslContext.getSocketFactory
}
}
为什么要使用无密码保护的私钥
val password = "keypass"
val clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType)
clientKeyStore.load(null, null)
clientKeyStore.setCertificateEntry("cert", x509ClientCert)
clientKeyStore.setKeyEntry("key", loadKey(inputStreamKeyPath), password.toCharArray, Array[Certificate](x509ClientCert))
val kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
kmFactory.init(clientKeyStore, password.toCharArray)
在这段代码中,`password`被用于保护密钥库(KeyStore)中的私钥。这个密码通常应该是安全的,并且只有知道密码的人才能访问存储在密钥库中的私钥。
在这个例子中,密码被硬编码为"keypass",这在实际的生产环境中是不安全的。硬编码的密码可能会被恶意用户发现,从而导致私钥的泄露。在实际的应用中,你应该使用更安全的方式来存储和管理密码,例如使用密码管理器,或者将密码存储在环境变量中。
另外,如果你的私钥是没有密码保护的,那么在调用`setKeyEntry`方法时,可以将密码参数设置为null或者空数组。但是请注意,没有密码保护的私钥在被盗取时会更容易被恶意使用。
https://www.22.cn/help_292.html
为什么要使用无密码保护的私钥?
在云产品使用数字证书过程中需要用户提供私钥,如果用户的私钥是加载密码保护的,那么在云产品reload时将无法使用,导致证书解密失败,https失效,所以我们需要您提供无密码保护的私钥以及用此私钥生成的CSR文件。
在生成私钥时需要去掉密码保护,具体请参考:什么是公钥和私钥?
如果您的密钥已经有密码保护,可以通过如下方式去掉密码:
openssl rsa -in encryedprivate.key -out unencryed.key
encryedprivate.key是带密码保护的私钥,unencryed.key是去掉了密码的私钥,扩展名为key或者pem均可。
https://www.22.cn/help_292.html
在Linux上使用openssl生成CA认证文件并为服务器和客户端颁发CA签名证书
https://blog.csdn.net/weixin_64334766/article/details/128594460
http://www.hzhcontrols.com/new-792337.html
openssl 基础命令
/root/emqx-v2/etc
[root@centos etc]# cd /opt/soft/ssl_02/
[root@centos etc]# pwd
/opt/soft/ssl_01/m1
# rsa 无加密
openssl genrsa -out rsa_private.key 2048
openssl rsa -in rsa_private.key -pubout -out rsa_public.key
# rsa 加密-aes256
openssl genrsa -aes256 -passout pass:111111 -out rsa_aes_private.key 2048
openssl rsa -in rsa_aes_private.key -passin pass:111111 -pubout -out rsa_public.key
# 私钥转非加密
openssl rsa -in rsa_aes_private.key -passin pass:111111 -out rsa_private.key
# 私钥转加密
openssl rsa -in rsa_private.key -aes256 -passout pass:111111 -out rsa_aes_private2.key
# 私钥PEM转DER 未通过
openssl rsa -in rsa_private.key -outform der-out rsa_aes_private.der
# 查看私钥明细
openssl rsa -in rsa_private.key -noout -text
生成 RSA 私钥和自签名证书
openssl req -newkey rsa:2048 -nodes -keyout rsa_private.key -x509 -days 365 -out cert.crt
req是证书请求的子命令,-newkey rsa:2048 -keyout private_key.pem 表示生成私钥(PKCS8格式),-nodes 表示私钥不加密,若不带参数将提示输入密码;
-x509表示输出证书,-days365 为有效期,此后根据提示输入证书拥有者信息;
若执行自动输入,可使用-subj选项:
openssl req -newkey rsa:2048 -nodes -keyout rsa_private.key -x509 -days 365 -out cert.crt -subj "/C=CN/ST=GD/L=SZ/O=vihoo/OU=dev/CN=vivo.com/emailAddress=yy@vivo.com"
mqtt 生产者
package com.hs.fast.test
/**
* @Title:
* @BelongProjecet hs_flink_elephant
* @BelongPackage com.hs.fast.test
* @Description:
* @Copyright time company - Powered By 研发一部
* @Author: cw
* @Date: 2023-11-2 10:01
* @Version V1.0
* ssl 认证
*/
import java.util.Properties
import com.hs.fast.bean.MqttConfig
import com.hs.fast.utils.PropertiesLoad2.getAllconf
import com.hs.fast.utils.mqtt.MySslUtil
import javax.net.ssl._
import org.eclipse.paho.client.mqttv3._
import play.api.libs.json.Json
object MqttClientExample4 extends App {
val clientId = "mqtt-client-1"
val topic = "topicwg3"
val ssl_url="ssl://ip:8883"
val getAllconfResult: (String, Properties) = getAllconf
val props2: Properties = getAllconfResult._2
lazy val mqttConfig = MqttConfig(
// props2.getProperty("m2.mqtt.url-ssl"),
ssl_url,
props2.getProperty("m2.mqtt.username"),
props2.getProperty("m2.mqtt.password"),
props2.getProperty("m2.mqtt.topic1")
)
// val r1 = props2.getProperty("m2.mqtt.ca")
// val r2 = props2.getProperty("m2.mqtt.client")
// val r3 = props2.getProperty("m2.mqtt.clientKey")
val r1 = "D:\\360Downloads\\ssl_02\\m2\\ca.pem"
val r2 = "D:\\360Downloads\\ssl_02\\m2\\client-scala.pem"
val r3 = "D:\\360Downloads\\ssl_02\\m2\\client-scala.key"
// val sSLSocketFactory: SSLSocketFactory = SslUtil.getSocketFactory(r1, r2, r3)
val sSLSocketFactory: SSLSocketFactory = MySslUtil.getSocketFactory(r1, r2, r3)
val options = new MqttConnectOptions()
options.setCleanSession(true)
options.setUserName("admin")
options.setPassword("admin".toCharArray)
options.setSocketFactory(sSLSocketFactory)
val client = new MqttClient(mqttConfig.url, clientId)
client.setCallback(new MqttCallback {
override def connectionLost(cause: Throwable): Unit = {
println("Connection lost: " + cause.getMessage)
}
override def messageArrived(topic: String, message: MqttMessage): Unit = {
println("Message received: " + new String(message.getPayload))
}
override def deliveryComplete(token: IMqttDeliveryToken): Unit = {
println("Delivery complete")
}
})
client.connect(options)
// 循环发送JSON数据
var counter = 0
while (true) {
val json = Json.obj(
"counter" -> counter,
"message" -> "Hello MQTT"
)
val payload = json.toString().getBytes("UTF-8")
val message = new MqttMessage(payload)
client.publish(topic, message)
println(s"Published message: $json")
counter += 1
Thread.sleep(1000) // 每隔1秒发送一次
}
// client.subscribe(topic, 1)
// Wait for messages
// Thread.sleep(5000)
// client.disconnect()
}
https://www.emqx.io/
https://github.com/emqx/emqx
https://github.com/mcxiaoke/mqtt