Adobe AIRのp12で、AndroidのAPKに署名できない

Adobe AIRで開発されたアプリがあり、そのアプリをAndroid Studioで作り直したことがあります。

そのアプリはp12ファイルで署名されていました。Android Studioでキーストアファイルにp12ファイルを設定して、署名付きAPKを作成してみましたが、APKファイルには署名がついていませんでした。

そのアプリは2013年後半に公開されたので、p12ファイルもその頃作成されたようです。

(1)keytoolでfingerprint

keytoolでfingerprintを表示しようとしたら、次のエラーが。

$ keytool -list -v -keystore a.p12 -storetype pkcs12
キーストアのパスワードを入力してください:  
keytoolエラー: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: DerInputStream.getLength(): Redundant length bytes foundCode language: Bash (bash)

試しに JKS に変換しようとしましたが、同じエラーでした。

$ keytool -importkeystore -keystore b.jks -srckeystore a.p12 -srcstoretype PKCS12
キーストアのパスワードを入力してください:  
keytoolエラー: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: DerInputStream.getLength(): Redundant length bytes foundCode language: Bash (bash)

「DerInputStream.getLength(): Redundant length bytes found」で検索しても、約500件しかヒットしません。いくつかの記事を見ると、JDK 7で署名キーを作成し、JDK 8で署名しようとしたら、エラーになったという内容でした。

他にも調べたなかで、参考になりそうな記事をピックアップすると、

We had the same problem. We have found that JDK 1.8.0_112 doesn't have the bug that you're talking about. So we resolved the problem in this way:
(Google翻訳)
同じ問題がありました。 JDK 1.8.0_112には、あなたが話しているバグがないことがわかりました。 そこで、この方法で問題を解決しました。

Signing android app throws IOException: Redundant length bytes found

stack overflowの回答者は、JDK 1.8.0_112のkeytoolでJKSに変換できたそうです。

Cause
The source keystore was created using an IBM SDK, and the import is attempting to use OpenJDK which results in the 'Invalid keystore format' error.
(Google翻訳)
原因
ソースキーストアはIBM SDKを使用して作成されましたが、インポートはOpenJDKを使用しようとしているため、「無効なキーストア形式」エラーが発生します。

Imports using keytool fails with "Invalid keystore format" error

今回のエラーとは関係ないようですが、keytoolのJDKのバージョンによって、こういうエラーが起きる可能性があるようです。

Java 8 Update 121 (8u121)
DERエンコーディング解析コードにチェックが追加されます
様々なエンコーディング・エラーを捕捉するために、DERエンコーディング解析コードにチェックが追加されます。また、構成済の不確定な長さのエンコーディングを含む署名により、解析中にIOExceptionが発生します。JDKデフォルト・プロバイダを使用して生成された署名はこの変更の影響を受けないことに注意してください。JDK-8168714 (非公開)

Java 8リリースのハイライト

今回のエラーは「java.io.IOException: DerInputStream.getLength(): Redundant length bytes found」なので、これが原因かもしれません。

(2)いくつかのJDKでfingerprintを試す

オリジナルp12をいくつかのJDKで試してみました。

JDKfingerprint表示JKSへ変換
Oracle JDK 1.7.0_80成功成功
openjdk-1.6.0_41エラーエラー
openjdk-1.7.0_201エラーエラー
openjdk-1.8.0_112成功成功
openjdk-1.8.0_212エラーエラー

変換したJKSを各JDKでfingerprint表示してみました。

JDKfingerprint表示
Oracle JDK 1.7.0_80成功
openjdk-1.6.0_41エラー
openjdk-1.7.0_201エラー
openjdk-1.8.0_112成功
openjdk-1.8.0_212エラー

Oracle JDK 1.7.0_80とopenjdk-1.8.0_112で、fingerprint表示とJKS変換をすることができました。

openjdk-1.8.0_112は、Android Studio アーカイブのAndroid Studio 2.3同梱のJDKです。

(3)解決方法

(3−1)Android StudioのJDK location

Android StudioのFile>Project structure...>SDK location>JDK locationに、Android Studio 2.3(openjdk-1.8.0_112)のjreを設定しました。

Build>Generate Signed APKで、オリジナルp12を指定して、署名付きAPKを作成してみました。APKの署名とJKSの署名が同じことを確認しました。

Build>Generate Signed APKで、変換後のJKSを指定して、署名付きAPKを作成してみました。APKの署名とJKSの署名が同じことを確認しました。

APKの署名のためとはいえ、Android StudioのJDKを変更するのは、どうも心配です。筆者は、開発環境やビルドの仕組みについて理解していないので、なるべくデフォルト設定を変更しないようにしています。

(3−2)環境変数のPATHやJAVA_HOME

実際の運用では、リリースAPK作成やGoogle Play ConsoleへのAPKアップロードはJenkinsで処理したいところです。

そのためには、Android StudioでリリースAPK作成するのではなく、ターミナルで ./gradlew app:assembleRelease を実行して、リリースAPKを作成します。

ターミナルを開き、環境変数のPATHやJAVA_HOMEをAndroid Studio 2.3のjreに設定しました。

./gradlew app:assembleRelease でAPK作成してみました。

APKの署名とJKSの署名が同じことを確認しました。

しかし、この方法は、開発中のAndroid StudioのJDKと、リリースビルド用のJDKが違うことになるのが、気になります。

(3−3)jarsignerでAPK署名

そこで、Android Studio同梱のJDK(openjdk-1.8.0_112)を使ってリリースAPKを作成した後、Android Studio 2.3(openjdk-1.8.0_112)のkeytoolやjarsignerを使って、APKに署名する方法も調べました。

keytoolはjreの下、zipalignはAndroid SDKのbuild-toolsの下にあります。

#!/bin/bash
set -u
set -e

# 署名なしのリリースAPKを作成する
./gradlew app:assembleRelease

export storeFile=aaaa.p12
export storePassword=xxxx
export keyPassword=yyyy
export keyAlias=zzzz

# jarsigner, keytool, zipalign
export PATH=~/android-studio-2.3/jre/bin:$ANDROID_HOME/build-tools/25.0.3:$PATH

apk_dir=app/build/outputs/apk/release
build_apk=$(ls $apk_dir/*.apk)
signed_apk=$build_apk.signed.apk
aligned_apk=$signed_apk.aligned.apk

cp $build_apk $signed_apk
jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore $storeFile -storetype pkcs12 -storepass $storePassword -keypass $keyPassword $signed_apk $keyAlias
zipalign -v 4 $signed_apk $aligned_apk
keytool -list -printcert -jarfile $aligned_apk
Code language: Bash (bash)

最終的には、この方法を採用しました。

(3−4)Google Play App Signing

従来からのアプリ署名の方法は、開発者が、アプリ署名キーでAPKに署名して、Google Playにアップロードしていました。

Google Play App Signingは、アップロード署名キーとアプリ署名キーの2つを使います。

1.開発者は、アップロード署名キーでAPKに署名して、Google Playにアップロードします。

2.Google Playが、アプリ署名キーでAPKに署名して、ストアに公開します。

2のアプリ署名キーは、従来のアプリ署名キーです。あらかじめ、pepk.jarでエクスポートしてから、Google Playに登録しておきます。

オリジナルp12をpepk.jarでエクスポートできるか試しました。

JDKキーストア結果
1openjdk-1.8.0_112オリジナルp12エラーなし
2openjdk-1.8.0_112変換後JKSエラーなし
3openjdk-1.8.0_212オリジナルp12java.io.IOException: Invalid keystore format
4openjdk-1.8.0_212変換後JKSjava.io.IOException: DerInputStream.getLength()

openjdk-1.8.0_112で、オリジナルp12をエクスポートすることができました。

しかし、openjdk-1.8.0_212 + オリジナルp12で「Invalid keystore format」になったことが気になります。

openjdk-1.8.0_112でも、オリジナルp12ではなくJKSを、pepk.jarでエクスポートしたほうがいいかもしれません。

タイトルとURLをコピーしました