在 Android 上使用自签名密钥库获取 SSLContext

问题描述 投票:0回答:1

我想让我的 Android 应用程序成为服务器。我有一个 SocketServerManger 类,从“org.java-websocket:Java-WebSocket:1.5.6”库实现 WebSocketServer。

Java-WebSocket

当我使用 ws 连接连接到我的服务器时,它可以正常工作。问题是当我的客户端想要使用 wss 连接来连接它时。 这是我的代码:

class SocketServerManger(
port: Int,
val socketListener: List<WebSocketConnectionListener>,
context: Context):WebSocketServer(InetSocketAddress(port)) {

var connection: WebSocket? = null

private val _isConnectionOpen = MutableStateFlow(false)
val isConnectionOpen: StateFlow<Boolean> = _isConnectionOpen

/*this part is added to set ssl context . without this part server works correctly with ws connection*/

init {
    createSSLContext(context)?.let {
        setWebSocketFactory(DefaultSSLWebSocketServerFactory(it))
    }

}

private fun createSSLContext(context: Context): SSLContext? {
    try {
        val keystoreFileName = "keystore.bks"
        val keystorePassword = "password"
        val keystoreInputStream: InputStream = context.assets.open(keystoreFileName)

        val keyStore = KeyStore.getInstance("BKS")
        keyStore.load(keystoreInputStream, keystorePassword.toCharArray())
        keystoreInputStream.close()

       
       val keyManagerFactory =
            KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
        keyManagerFactory.init(keyStore, keystorePassword.toCharArray())


       
        val tmf : TrustManagerFactory = TrustManagerFactory.getInstance("BKS");
        tmf.init(keyStore)

        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, tmf.trustManagers, SecureRandom())
        return sslContext
    } catch (e: Exception) {
        serverLog("createSSLContext ${e.message}")
    }
    return null
}

/*end of setup ssl*/

override fun onStart() {

    serverLog("SocketServerManger onStart")
    println("WebSocket server started")
}

override fun onOpen(conn: WebSocket?, handshake: ClientHandshake?) {
    connection = conn
    socketListener.forEach { it.onConnected() }
}

override fun onClose(conn: WebSocket?, code: Int, reason: String?, remote: Boolean) {
   
    socketListener.forEach { it.onDisconnected(code, reason) }
}

override fun onMessage(conn: WebSocket?, message: String?) {
   
    socketListener.forEach { it.onMessage(conn, message) }

}

override fun onError(conn: WebSocket?, ex: Exception?) {
    socketListener.forEach { it.onError(ex) }
    ex?.printStackTrace()
}


suspend fun sendMessagesUntilSuccess(timeoutMillis: Long = 50000, message: String): Boolean {
    var success = false
    while (!success) {
        
        success = sendMessageWithTimeout(timeoutMillis, message)
        if (!success) {
           
            // Add a delay before retrying to avoid continuous retries and potential rate limiting
            delay(1000)
        }
    }
    return success
}

suspend fun sendMessageWithTimeout(timeoutMillis: Long = 5000, message: String): Boolean {
    return withContext(Dispatchers.IO) {
        return@withContext try {
            withTimeout(timeoutMillis) {
                val result = CoroutineScope(Dispatchers.IO).async {
                    try {
                        if (connection != null && connection!!.isOpen) {
                            connection!!.send(message)
                            true
                        } else {
                           
                            false
                        }
                    } catch (e: org.java_websocket.exceptions.WebsocketNotConnectedException) {
                        socketListener.forEach { it.onException(e) }
                        false
                    }
                }.await()

                result
            }
        } catch (e: TimeoutCancellationException) {
            socketListener.forEach { it.onException(e) }
            false
        } catch (e: Exception) {
            socketListener.forEach { it.onException(e) }
            false
        }
    }
}


fun isConnectionOpen() = connection?.isOpen ?: false

fun isPortAvailable(port: Int): Boolean {
    return try {
        ServerSocket(port).close()
        true
    } catch (e: IOException) {
        false
    }
}

}

当我想使用 Web 客户端连接到服务器时,连接超时。

android kotlin ssl websocket server
1个回答
0
投票

使用自签名证书在 Android 上设置 ssl 需要一些步骤,应正确遵循这些步骤,否则您会遇到一些问题。

1/安装OpenSSL

2/生成私钥

使用OpenSSL生成私钥

openssl genrsa -out key.pem 2048 

3/ 创建证书签名请求 (CSR) 使用私钥创建 CSR

openssl req -new -key key.pem -out csr.pem 

4/ 生成自签名证书 您可以使用私钥和 CSR 生成自签名证书。

openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out certificate.pem

5/ 将证书转换为适当的格式

android支持bks。首先,您应该将证书转换为 PKCS#12 格式,扩展名为 .p12 或 .pfx,然后转换为 bks 。

openssl pkcs12 -export -out certificate.p12 -inkey key.pem -in certificate.pem -name "alias"

keytool -importkeystore -srckeystore certificate.p12 -srcstoretype PKCS12 -destkeystore  certificate.bks -deststoretype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "D:\bcprov-debug-jdk18on-1.78.1.jar

6/ 将bks文件格式放入你的android项目中。我把它放在我的资产中。

7/ 在代码中设置 ssl :

` 初始化 {

createSSLContext(context)?.let {
    
    setWebSocketFactory(DefaultSSLWebSocketServerFactory(it))
}

}

private fun createSSLContext(context: Context): SSLContext? {

val keystoreFileName = "certificate.bks"
val keystorePassword = "yourpassword"
val keystoreInputStream: InputStream = context.assets.open(keystoreFileName)

try {

    val keystore = KeyStore.getInstance("BKS")

    keystore.load(keystoreInputStream, keystorePassword.toCharArray())

    val keyManagerFactory = KeyManagerFactory.getInstance("X509")
    keyManagerFactory.init(keystore, keystorePassword.toCharArray())
    val tmf : TrustManagerFactory = TrustManagerFactory.getInstance("X509");
    tmf.init(keystore)

    val sslContext = SSLContext.getInstance("TLS")
    sslContext.init(keyManagerFactory.keyManagers,tmf.trustManagers, null)
    return sslContext
} catch (e: Exception) {
  
    keystoreInputStream.close()
}
return null

}`

按照这些步骤最终实现与 wss 的连接。

© www.soinside.com 2019 - 2024. All rights reserved.