Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set up ssl cause throwing NoSuchAlgorithmException: KeyStore JKS implementation not found #1410

Closed
sepideh69a opened this issue May 5, 2024 · 4 comments

Comments

@sepideh69a
Copy link

sepideh69a commented May 5, 2024

my android app is going to be a server which allows a web to connect to it as a client . i have a SocketServerManger which handle the task properly when i connect to my android device via ws connection . but i want my clients can connect to the android device server with wss connection .
here is my manager :

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

var connection: WebSocket? = null

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


override fun 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) {
        serverLog(
            "while (!success)",
            "timeOutTag"
        )
        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 {
                        serverLog("sendMessageWithTimeout try : $message", "timeOutTag")
                        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
    }
}

}`

i add the below code to set up ssl/tls:

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

}

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

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

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

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

`
but it keeps throwing the exception:

Caused by: java.security.KeyStoreException: java.security.NoSuchAlgorithmException: KeyStore JKS implementation not found

it throw it on this line :

val keyStore = KeyStore.getInstance("JKS")

@sepideh69a sepideh69a changed the title connect with wss set up ssl cause throwing NoSuchAlgorithmException: KeyStore JKS implementation not May 5, 2024
@sepideh69a sepideh69a changed the title set up ssl cause throwing NoSuchAlgorithmException: KeyStore JKS implementation not set up ssl cause throwing NoSuchAlgorithmException: KeyStore JKS implementation not found May 5, 2024
@marci4
Copy link
Collaborator

marci4 commented May 5, 2024

Android only supports BKS as far as I remember

@sepideh69a
Copy link
Author

sepideh69a commented May 6, 2024

Android only supports BKS as far as I remember

i changed the keystore to bks and edit code as
` private fun createSSLContext(context: Context): SSLContext? {
try {
val keystoreFileName = "myketstore.bks"
val keystorePassword = "mypassword"
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 sslContext = SSLContext.getInstance("TLS")
        sslContext.init(keyManagerFactory.keyManagers, null, SecureRandom())
        return sslContext
    } catch (e: Exception) {
        serverLog("createSSLContext ${e.message}")
    }
    return null
}`

now the exception i talked about in previous post is solved but i cannot connect to server yet . it fails .
when i test it with postman it gives the error
Client network socket disconnected before secure TLS connection was established

@marci4
Copy link
Collaborator

marci4 commented May 7, 2024

See https://github.com/TooTallNate/Java-WebSocket/wiki/Getting-a-SSLContext-from-different-sources#getting-a-sslcontext-using-a-keystore-on-android for an android example.

Apart from this, I dont think we can help you much here.
This question are something for stackoverflow.

@SepidehAkbarinezhad
Copy link

SepidehAkbarinezhad commented May 13, 2024

i figured it out .
setting up ssl on android requirs some steps which should be followed correctly otherwise you encounter some issues.

1/ Install OpenSSL

2/Generate a Private Key

Use OpenSSL to generate a private key
openssl genrsa -out key.pem 2048

3/ Create a Certificate Signing Request (CSR)
Use the private key to create a CSR

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

4/ Generate a Self-Signed Certificate
You can generate a self-signed certificate using the private key and CSR.

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

5/ Convert the Certificate to the Appropriate Format

android supports bks . first you should convert the certificate to PKCS#12 format with the extension .p12 or .pfx then to 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/ put the bks file format in your android project . i put it in my assets .

7/ set up ssl in your code :

` init {

    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
}`

following these steps eventually resulted in connecting with wss.
hope help someone :)

@marci4 marci4 closed this as completed May 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants