mgth/LittleBigMouse · error · System.Security.Authentication.AuthenticationException

The C1 requires the VIDAA client certificate. Extract the…

Error message

The C1 requires the VIDAA client certificate. Extract the .p12 from the official Android APK and copy it to {DefaultPath}.

What it means

During VIDAA MQTT ConnectAsync, the TCP read of the CONNACK packet can surface an IOException. The filter `when (certificates.Count == 0)` distinguishes a TLS handshake failure caused by an absent client certificate from other IO faults, and rethrows it as VidaaCertificate.MissingException(e). The message tells the user to extract the .p12 from the official Android APK and copy it to the default path.

Solutions

  1. Extract the .p12 from the official Hisense Android APK and place it at the DefaultPath, then retry the connection.
  2. Point ClientCertificatePath in HisenseVidaaConfiguration at the actual .p12 file and confirm File.Exists passes.
  3. Check file permissions on the .p12 (must be readable by the daemon user).
  4. Confirm the certificate loaded successfully before connecting (e.g. inspect certificates.Count / certificate load logs) to rule out a load-time failure rather than a missing file.

Example fix

// before
await OpenMqttAsync(config, ct); // throws MissingException
// after
var certPath = VidaaCertificate.Resolve(config.ClientCertificatePath);
if (string.IsNullOrWhiteSpace(certPath) || !File.Exists(certPath))
    throw new InvalidOperationException($"Copy the extracted .p12 to {VidaaCertificate.DefaultPath} before connecting.");
Defensive patterns

Strategy: try-catch

Validate before calling

// before connecting
var certPath = VidaaCertificate.Resolve(config.ClientCertificatePath);
if (string.IsNullOrWhiteSpace(certPath) || !File.Exists(certPath))
    throw new InvalidOperationException($"Copy the extracted .p12 to {VidaaCertificate.DefaultPath} before connecting.");

Try / catch

try { await connection.OpenMqttAsync(config, ct); }
catch (Exception e) when (e.Message.Contains("VIDAA client certificate")) {
    // cert absent -> surface setup instructions, do not retry blind
    ui.PromptCertificateInstall(VidaaCertificate.DefaultPath);
}

Prevention

When it happens

Trigger: Calling ConnectAsync (via OpenMqttAsync) when the TLS client-hello carries no client certificate (certificates.Count == 0) and the device/stack aborts the handshake with an IOException — i.e. the .p12 was never loaded because it is missing at the configured path.

Common situations: Same as the pre-flight MissingException but detected later: certificate file deleted after configuration was saved; path points to an unreadable/empty file so no certificate loaded; container without the cert mounted; stale config after app reinstall.

Understand the failure class

Related errors


AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16). Data as JSON: /api/errors/506d186f194e9108. Report an issue: GitHub.

Appendix: source

Thrown at LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/HisenseVidaa/VidaaMqttConnection.cs:83

            }, cancellationToken).ConfigureAwait(false);
        }
        catch (AuthenticationException e) when (certificates.Count == 0)
        {
            throw new AuthenticationException(
                "VIDAA rejected TLS. Select the PKCS#12 client certificate extracted from the official app.", e);
        }

        _stream = ssl;
        await WritePacketAsync(BuildConnectPacket(clientId, username, password), cancellationToken)
            .ConfigureAwait(false);
        (byte Header, byte[] Payload) connack;
        try
        {
            connack = await ReadPacketAsync(_stream, cancellationToken).ConfigureAwait(false);
        }
        catch (IOException e) when (certificates.Count == 0)
        {
            throw VidaaCertificate.MissingException(e);
        }
        if ((connack.Header >> 4) != 2 || connack.Payload.Length < 2)
            throw new IOException("VIDAA returned an invalid MQTT connection response.");
        if (connack.Payload[1] != 0)
            throw new UnauthorizedAccessException(MqttError(connack.Payload[1]));

        _connected = true;
        // The caller token limits the handshake only. Once established, MQTT has
        // its own lifetime so a completed UI command cannot tear down the session.
        _lifetime = new CancellationTokenSource();
        _readerTask = ReadLoopAsync(_lifetime.Token);
        _pingTask = PingLoopAsync(_lifetime.Token);
    }

    public async Task SubscribeAsync(IEnumerable<string> topics, CancellationToken cancellationToken)
    {
        foreach (var topic in topics)
        {

View on GitHub (pinned to 7a42f01d47)