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
  }

}



image

为什么要使用无密码保护的私钥

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
image

在Linux上使用openssl生成CA认证文件并为服务器和客户端颁发CA签名证书
https://blog.csdn.net/weixin_64334766/article/details/128594460
image
http://www.hzhcontrols.com/new-792337.html
image
image
image
image
image
image

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

posted @ 2023-11-07 16:18  三里清风18  阅读(38)  评论(0)    收藏  举报