Сертификаты минцифры в Firefox Nightly на Android: через adb, без рута
Понадобилось, чтобы сертификату минцифры доверял только Firefox Nightly, а не весь Android.
GeckoView читает /data/local/tmp/<package>-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.