Сертификаты минцифры в Firefox Nightly на Android: через adb, без рута

Posted on Сб 15 августа 2026 in misc

Источник

Понадобилось, чтобы сертификату минцифры доверял только Firefox Nightly, а не весь Android.

GeckoView читает /data/local/tmp/-geckoview-config.yaml не только у debuggable-приложений, но и если пакет назначен текущим debug_app. А в конфиге можно задать аргументы запуска Gecko и поднять Marionette.

Поднимаем Marionette

geckoview-config.yaml:

args:
  - --marionette
  - --remote-allow-system-access
prefs:
  marionette.port: 2828
adb shell am set-debug-app --persistent org.mozilla.fenix
adb push geckoview-config.yaml \
  /data/local/tmp/org.mozilla.fenix-geckoview-config.yaml
adb shell chmod 644 /data/local/tmp/org.mozilla.fenix-geckoview-config.yaml
adb shell am force-stop org.mozilla.fenix
adb shell monkey -p org.mozilla.fenix -c android.intent.category.LAUNCHER 1
adb forward tcp:2828 tcp:2828

Кладём сертификаты в cert9.db

Marionette в chrome-контексте даёт привилегированный JS, дальше всё делает nsIX509CertDB. Флаг "C,," для корня, которому доверяем по SSL, ",," для промежуточных: доверия им не надо, они нужны только чтобы собралась цепочка.

Протокол простой, длина:json поверх TCP, клиент пишется за пять минут:

use v5.36;
use IO::Socket::INET;
use MIME::Base64 'encode_base64';
use JSON::PP;

my $s = IO::Socket::INET->new('127.0.0.1:2828') or die $@;
my $j = JSON::PP->new->canonical;
my $id = 0;

sub pkt {
    my ($len, $buf, $c) = ('', '');
    $len .= $c while sysread($s, $c, 1) and $c ne ':';
    $buf .= $c
        while length $buf < $len and sysread $s, $c, $len - length $buf;
    $j->decode($buf);
}

sub cmd ($name, $params = {}) {
    my $body = $j->encode([0, ++$id, $name, $params]);
    print {$s} length($body) . ":$body";
    my $r = pkt();
    die "$name: " . $j->encode($r->[2]) . "\n" if defined $r->[2];
    $r->[3];
}

my @certs = map {
    my ($file, $trust) = split /=/, $_, 2;
    open my $fh, '-|', qw(openssl x509 -outform DER -in), $file
        or die "$file: $!";
    binmode $fh;
    [ encode_base64(do { local $/; <$fh> }, ''), $trust // 'C,,' ];
} @ARGV;

pkt();
cmd 'WebDriver:NewSession';
cmd 'Marionette:SetContext', { value => 'chrome' };

say JSON::PP->new->pretty->canonical->encode(cmd 'WebDriver:ExecuteScript', {
    script => q{
        const cid = "@mozilla.org/security/x509certdb;1";
        const db = Cc[cid].getService(Ci.nsIX509CertDB);
        return arguments[0].map(([b64, trust]) => {
            try {
                const c = db.addCertFromBase64(b64, trust);
                return { ok: true, subject: c.subjectName };
            } catch (e) {
                return { ok: false, error: String(e) };
            }
        });
    },
    args => [ \@certs ],
});

cmd 'WebDriver:DeleteSession';

устанавливем:

perl fenix-cert.pl \
  russian_trusted_root_ca_pem.crt=C,, \
  russian_trusted_sub_ca_pem.crt=,, \
  russian_trusted_sub_ca_2024_pem.crt=,,

# cleanup
adb shell rm -f /data/local/tmp/org.mozilla.fenix-geckoview-config.yaml
adb shell am clear-debug-app
adb forward --remove tcp:2828
adb shell am force-stop org.mozilla.fenix

Проверено на Pixel 5, Android 14, Firefox Nightly 156.0a1.