153 lines
11 KiB
HTML
153 lines
11 KiB
HTML
|
<!doctype html>
|
||
|
<html lang="en-gb">
|
||
|
<head>
|
||
|
<meta charset="UTF-8" />
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
|
<link href="https://blog.polynom.me/css/index.css" rel="stylesheet" integrity="sha384-EJX4ZZbYMJeuoLRp93IbM/RYSzZmxw42TK7sgSRBEMChbBFK4NYUQEfsz3nBJQm8" />
|
||
|
|
||
|
|
||
|
<link rel="alternate" type="application/rss+xml" title="blog.polynom.me Atom feed" href="https://blog.polynom.me/atom.xml">
|
||
|
|
||
|
|
||
|
|
||
|
<meta property="og:description" content="" />
|
||
|
<meta property="og:title" content="Signing Android Apps Using a YubiKey (on NixOS)" />
|
||
|
<title>Signing Android Apps Using a YubiKey (on NixOS)</title>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</head>
|
||
|
<body>
|
||
|
<div class="flex flex-col p-2 md:p-8 items-start md:w-4/5 mx-auto">
|
||
|
<!-- Header -->
|
||
|
<div class="flex flex-row self-center">
|
||
|
<img class="w-12 h-12 md:w-24 md:h-24 rounded-lg" src="https://blog.polynom.me/img/avatar.jpg" integrity="sha384-uiNteVXosQ2+o/izp41L1G9VwuwYDYCOPxzFWks058DMUhW7KfQXcipM7WqgSgEZ" alt="Profile picture"/>
|
||
|
<div class="ml-4 self-center">
|
||
|
<a class="self-center text-2xl font-bold" href="/">PapaTutuWawa's Blog</a>
|
||
|
|
||
|
<ul class="list-none">
|
||
|
<li class="inline mr-8"><a href="/">Posts</a></li>
|
||
|
<li class="inline mr-8"><a href="https://blog.polynom.me/atom.xml">RSS</a></li>
|
||
|
<li class="inline mr-8"><a href="https://polynom.me">About</a></li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<!-- Container for posts -->
|
||
|
<div class="mx-auto mt-4 w-full md:max-w-prose">
|
||
|
<h1 class="text-indigo-400 text-3xl">Signing Android Apps Using a YubiKey (on NixOS)</h1>
|
||
|
|
||
|
<span class="text-md mt-2">Posted on 2023-07-24</span>
|
||
|
|
||
|
|
||
|
|
||
|
<!-- Actual article -->
|
||
|
<article class="prose lg:prose-lg text-white mt-4">
|
||
|
<p>In my spare time, I currently develop two Android apps using <em>Flutter</em>: <a href="https://codeberg.org/PapaTutuWawa/anitrack">AniTrack</a>, a
|
||
|
simple anime and manga tracker based on my own needs, and <a href="https://moxxy.org">Moxxy</a>, a modern XMPP
|
||
|
client. While I don't provide release builds for AniTrack, I do for Moxxy. Those
|
||
|
are signed using the key-pair that Flutter generates. I thought to myself: "Wouldn't it be cool if I could keep
|
||
|
the key-pair on a separate device which does the signing for me?". The consequence
|
||
|
of this thought is that I bought a <em>YubiKey 5c</em>. However, as always, using it for my
|
||
|
purposes did not go without issues.</p>
|
||
|
<span id="continue-reading"></span>
|
||
|
<p>The first issue is that the official <a href="https://developer.android.com/build/building-cmdline#deploy_from_bundle"><em>Android</em> documentation</a>
|
||
|
says to use the <code>apksigner</code> tool for creating the signature. <a href="https://developers.yubico.com/PIV/Guides/Android_code_signing.html">The <em>YubiKey</em> documentation</a>, however,
|
||
|
uses <code>jarsigner</code>. While I, at first, did not think much of it, <em>Android</em> has
|
||
|
<a href="https://source.android.com/docs/security/features/apksigning/">different versions of the signature algorithm</a>: <code>v1</code> (what <code>jarsigner</code> does), <code>v2</code>, <code>v3</code>, <code>v3.1</code> and
|
||
|
<code>v4</code>. While it seems like it would be no problem to just use <code>v1</code> signatures, <em>Flutter</em>, by default,
|
||
|
generates <code>v1</code> and <code>v2</code> signatures, so I thought that I should keep it like that.</p>
|
||
|
<p>So, the solution is to just use <code>apksigner</code> instead of <code>jarsigner</code>, like <a href="https://geoffreymetais.github.io/code/key-signing/">another person on the Internet</a> did.
|
||
|
But that did not work for me. Running <code>apksigner</code> like that makes it complain that <code>apksigner</code> cannot
|
||
|
access the required <code>sun.security.pkcs11.SunPKCS11</code> Java class.</p>
|
||
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>> /nix/store/ib27l0593bi4ybff06ndhpb8gyhx5zfv-android-sdk-env/share/android-sdk/build-tools/34.0.0/apksigner sign \
|
||
|
</span><span> --ks NONE \
|
||
|
</span><span> --ks-pass "pass:<YubiKey PIN>" \
|
||
|
</span><span> --provider-class sun.security.pkcs11.SunPKCS11 \
|
||
|
</span><span> --provider-arg ./provider.cfg \
|
||
|
</span><span> --ks-type PKCS11 \
|
||
|
</span><span> --min-sdk-version 24 \
|
||
|
</span><span> --max-sdk-version 34 \
|
||
|
</span><span> --in unsigned.apk \
|
||
|
</span><span> --out signed.apk
|
||
|
</span><span>
|
||
|
</span><span>Exception in thread "main" java.lang.IllegalAccessException: class com.android.apksigner.ApkSignerTool$ProviderInstallSpec cannot access class sun.security.pkcs11.SunPKCS11 (in module jdk.crypto.cryptoki) because module jdk.crypto.cryptoki does not export sun.security.pkcs11 to unnamed module @75640fdb
|
||
|
</span><span> at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
|
||
|
</span><span> at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
|
||
|
</span><span> at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:489)
|
||
|
</span><span> at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
|
||
|
</span><span> at com.android.apksigner.ApkSignerTool$ProviderInstallSpec.installProvider(ApkSignerTool.java:1233)
|
||
|
</span><span> at com.android.apksigner.ApkSignerTool$ProviderInstallSpec.access$200(ApkSignerTool.java:1201)
|
||
|
</span><span> at com.android.apksigner.ApkSignerTool.sign(ApkSignerTool.java:343)
|
||
|
</span><span> at com.android.apksigner.ApkSignerTool.main(ApkSignerTool.java:92)
|
||
|
</span></code></pre>
|
||
|
<p>It may only be an issue because I use NixOS, as I
|
||
|
cannot find another instance of someone else having this issue. But I still want my APK signed using the key-pair
|
||
|
on my <em>YubiKey</em>. After a lot of trial and error, I found out that I can force Java to export certain classes
|
||
|
using the <code>--add-exports</code> flag. Since <code>apksigner</code> complained that the security classes are not exported to its
|
||
|
unnamed class, I had to specify <code>--add-exports sun.security.pkcs11.SunPKCS11=ALL-UNNAMED</code>.</p>
|
||
|
<h2 id="my-setup">My Setup</h2>
|
||
|
<p>TL;DR: I wrapped this entire setup (minus the Gradle config as that's a per-project thing) into a fancy <a href="https://codeberg.org/PapaTutuWawa/bits-and-bytes/src/branch/master/src/flutter/build.sh">script</a>.</p>
|
||
|
<p>My provider configuration for the signature is exactly like the one provided in <a href="https://geoffreymetais.github.io/code/key-signing/#set-up-your-own-management-key">previously mentioned blog post</a>,
|
||
|
with the difference that I cannot use the specified path to the <code>opensc-pkcs11.so</code> as I am on NixOS, where such
|
||
|
paths are not used. So in my setup, I either use the Nix REPL to build the derivation for <code>opensc</code> and then
|
||
|
use its <code>lib/opensc-pkcs11.so</code> path (<code>/nix/store/h2bn9iz4zqzmkmmjw9b43v30vhgillw4-opensc-0.22.0</code> in this case) for testing or, as
|
||
|
used in <a href="https://codeberg.org/PapaTutuWawa/anitrack/src/branch/master/flake.nix">AniTrack</a>, let Nix figure out the path by building
|
||
|
the config file from within my Nix Flake:</p>
|
||
|
<pre data-lang="nix" style="background-color:#2b303b;color:#c0c5ce;" class="language-nix "><code class="language-nix" data-lang="nix"><span>{
|
||
|
</span><span> </span><span style="color:#65737e;"># ...
|
||
|
</span><span> </span><span style="color:#d08770;">providerArg </span><span>= </span><span style="color:#bf616a;">pkgs</span><span>.</span><span style="color:#bf616a;">writeText </span><span>"</span><span style="color:#a3be8c;">provider-arg.cfg</span><span>" ''
|
||
|
</span><span style="color:#a3be8c;"> name = OpenSC-PKCS11
|
||
|
</span><span style="color:#a3be8c;"> description = SunPKCS11 via OpenSC
|
||
|
</span><span style="color:#a3be8c;"> library = </span><span style="font-style:italic;color:#ab7967;">${</span><span style="font-style:italic;color:#bf616a;">pkgs</span><span style="font-style:italic;color:#c0c5ce;">.</span><span style="font-style:italic;color:#bf616a;">opensc</span><span style="font-style:italic;color:#ab7967;">}</span><span style="color:#a3be8c;">/lib/opensc-pkcs11.so
|
||
|
</span><span style="color:#a3be8c;"> slotListIndex = 0
|
||
|
</span><span style="color:#a3be8c;"> </span><span>'';
|
||
|
</span><span> </span><span style="color:#65737e;"># ...
|
||
|
</span><span>}
|
||
|
</span></code></pre>
|
||
|
<p>Next, to force Java to export the <code>sun.security.pkcs11.SunPKCS11</code> class to <code>apksigner</code>'s unnamed class, I added <code>--add-exports sun.security.pkcs11.SunPKCS11</code>
|
||
|
to the Java command line. There are two ways of doing this:</p>
|
||
|
<ol>
|
||
|
<li>Since <code>apksigner</code> is just a wrapper script around calling <code>apksigner.jar</code>, we could patch the wrapper script to include this parameter.</li>
|
||
|
<li>Use the wrapper script's built-in mechanism to pass arguments to the <code>java</code> command.</li>
|
||
|
</ol>
|
||
|
<p>While option 1 would work, it would require, in my case, to override the derivation that builds my Android SDK environment, which I am not that fond of.
|
||
|
Using <code>apksigner</code>'s way of specifying Java arguments (<code>-J</code>) is much easier. However, there is a little trick to it: When you pass <code>-Jsomething</code> to <code>apksigner</code>,
|
||
|
the wrapper scripts transforms it to <code>java -something</code>. As such, we cannot pass <code>-Jadd-exports sun.security.pkcs11.SunPKCS11</code> because it would get transformed
|
||
|
to <code>java -add-exports sun.security.[...]</code>, which is not what we want. To work around this, I quote the entire parameter to trick Bash into thinking that I'm
|
||
|
passing a single argument: <code>-J"-add-exports sun.security.pkcs11.SunPKCS11"</code>. This makes the wrapper append <code>--add-exports sun.security.pkcs11.SunPKCS11</code> to the
|
||
|
Java command line, ultimately allowing me to sign unsigned Android APKs with the key-pair on my <em>YubiKey</em>.</p>
|
||
|
<p>Since signing a signed APK makes little sense, we also need to tell Gradle to <em>not</em> sign the APK. In the case of Flutter apps, I modified the <code>android/app/build.gradle</code>
|
||
|
file to use a null signing config:</p>
|
||
|
<pre data-lang="gradle" style="background-color:#2b303b;color:#c0c5ce;" class="language-gradle "><code class="language-gradle" data-lang="gradle"><span>android {
|
||
|
</span><span> </span><span style="color:#65737e;">// ...
|
||
|
</span><span> buildTypes {
|
||
|
</span><span> release {
|
||
|
</span><span> </span><span style="color:#65737e;">// This prevents Gradle from signing release builds.
|
||
|
</span><span> </span><span style="color:#65737e;">// I don't care what happens to debug builds as I'm not distributing them.
|
||
|
</span><span> signingConfig </span><span style="color:#d08770;">null
|
||
|
</span><span> }
|
||
|
</span><span> }
|
||
|
</span><span>}
|
||
|
</span></code></pre>
|
||
|
|
||
|
</article>
|
||
|
|
||
|
<!-- Common post footer -->
|
||
|
<div class="mt-6">
|
||
|
<span class="prose lg:prose-lg text-md text-white">
|
||
|
If you have any questions or comments, then feel free to send me an email (Preferably with GPG encryption)
|
||
|
to papatutuwawa [at] polynom.me or reach out to me on the Fediverse at <a href="https://social.polynom.me/papatutuwawa">@papatutuwawa@social.polynom.me</a>.
|
||
|
</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
</div>
|
||
|
</body>
|
||
|
</html>
|