その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Maven でjar ファイル (apk ファイル) の署名を自動化する方法

前エントリで言いたい放題言っていましたが、Maven の話です。
この関連の情報は豊富にありますが、分散している感があるのでまとめました。

本題

ソフトウェアをリリースする際に必要な署名作業を、いちいち手動でやるのはだるいので、
それをMaven にやらせてしまおうという話です。
jar ファイルを題材として扱いますが、apk ファイルでも同様の手順で署名が可能です。

以下手順です。


1. 証明書 (keystore) を作成する
以下のコマンドで証明書 (keystore) を作成します。
$ keytool -genkey -keystore ./sample.keystore -validity 10000 -alias sample_alias
Enter keystore password:<任意のパスワード>
Re-enter new password:<任意のパスワードの繰り返し>
What is your first and last name?
[Unknown]: Kurt
What is the name of your organizational unit?
[Unknown]: Cobain
What is the name of your organization?
[Unknown]: NIRVANA
What is the name of your City or Locality?
[Unknown]: Seattle
What is the name of your State or Province?
[Unknown]: Washington
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=Kurt, OU=Cobain, O=NIRVANA, L=Seattle, ST=Washington, C=US correct?
[no]: yes

Enter key password for <sample_alias>
(RETURN if same as keystore password):<任意のパスワード>
これについては以下の記事が詳しいので参照されると良いと思います。
throw Life - Androidアプリに証明書を入れてちゃんとデジタル署名する方法


2. pom.xml を書く
<project>
  ...
    <profiles>
      ...
        <profile>
            <id>release</id>
            <build>
                <plugins>
                  ...
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jarsigner-plugin</artifactId>
                        <version>1.2</version>
                        <executions>
                            <execution>
                                <id>signing</id>
                                <goals>
	                            <goal>sign</goal>
		                    <goal>verify</goal>
	                        </goals>
	                        <phase>package</phase>
	                        <inherited>true</inherited>
	                        <configuration>
	                            <removeExistingSignatures>true</removeExistingSignatures>
	                            <archiveDirectory></archiveDirectory>
	                            <includes>
	                                <include>${project.build.directory}/${project.artifactId}.jar</include>
	                            </includes>
                                    <keystore>./sample.keystore</keystore>
	                            <storepass>キーストアのパスワード</storepass>
	                            <keypass>キーのパスワード</keypass>
	                            <alias>sample_alias</alias>
                                    <verbose>true</verbose>
	                        </configuration>
                            </execution>
                        </executions>
                    </plugin>
                  ...
                </plugins>
            </build>
        </profile>
      ...
    </profiles>
  ...
</project>
こんな感じでpom.xml を書いて、
$ mvn package -Prelease
とプロファイルを指定してmvn package すると自動的に署名されます。便利ですね。

なお、親pom でmaven-jarsigner-plugin のgroupId とversion を指定している場合は、
このpom で指定する必要はありません。むしろ親pom で指定した方が良いと思いますが、そこはお好みでお願いします。


3. 確認する
以下のコマンドを実行すると、jar ファイル(或いはapk ファイル) が署名済みであるかどうかを確認できます。
$ jarsigner -verify -verbose -certs sample.jar
コマンドの結果が成功したか失敗したかはフィーリングで分かると思います。(適当)


4. zipalign も自動化する
apk ファイルにはzipalign というナイスな最適化ツールがあって、それもMaven で自動的に適用出来るとベリーハッピーです。
pom.xml に以下のように書くとzipalign が適用されます。
<project>
  ...
    <profiles>
      ...
        <profile>
            <id>release</id>
            <build>
                <plugins>
                  ...
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jarsigner-plugin</artifactId>
                        <version>1.2</version>
                        <executions>
                            <execution>
                                <id>signing</id>
                                <goals>
	                            <goal>sign</goal>
		                    <goal>verify</goal>
	                        </goals>
	                        <phase>package</phase>
	                        <inherited>true</inherited>
	                        <configuration>
	                            <removeExistingSignatures>true</removeExistingSignatures>
	                            <archiveDirectory></archiveDirectory>
	                            <includes>
	                                <include>${project.build.directory}/${project.artifactId}.apk</include>
	                            </includes>
                                    <keystore>./sample.keystore</keystore>
	                            <storepass>キーストアのパスワード</storepass>
	                            <keypass>キーのパスワード</keypass>
	                            <alias>sample_alias</alias>
                                    <verbose>true</verbose>
	                        </configuration>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
			<artifactId>android-maven-plugin</artifactId>
                        <version>3.5.0</version>
			<inherited>true</inherited>
			<configuration>
			    <sign>
			        <debug>false</debug>
			    </sign>
			    <zipalign>
			        <verbose>true</verbose>
			        <inputApk>${project.build.directory}/${project.artifactId}.apk</inputApk>
			        <outputApk>${project.build.directory}/${project.artifactId}.signed-aligned.apk</outputApk>
			    </zipalign>
			</configuration>
			<executions>
			    <execution>
				<id>alignApk</id>
				<phase>package</phase>
				<goals>
				    <goal>zipalign</goal>
				</goals>
			    </execution>
			</executions>
		    </plugin>
		    <plugin>
		        <groupId>org.codehaus.mojo</groupId>
			<artifactId>build-helper-maven-plugin</artifactId>
                        <version>1.7</version>
			<configuration>
			    <artifacts>
			        <artifact>
			            <file>${project.build.directory}/${project.artifactId}.signed-aligned.apk</file>
			            <type>apk</type>
			            <classifier>signed-aligned</classifier>
				</artifact>
			    </artifacts>
			</configuration>
			<executions>
			    <execution>
			        <id>attach-signed-aligned</id>
				<phase>package</phase>
				<goals>
				    <goal>attach-artifact</goal>
				</goals>
			    </execution>
			</executions>
		    </plugin>
                  ...
                </plugins>
            </build>
        </profile>
      ...
    </profiles>
  ...
</project>
こんな感じでpom.xml を書いて、
$ mvn package -Prelease
とプロファイルを指定してmvn package すると自動的に署名とzipalign が効きます。これまた便利ですね。
このpom を使用すると署名済みかつzipalign が有効になった.signed-aligned.apk という名前のapk が生成されます。

なお、このpom も親pom にgroupId とversion が記述されていればよしなにやってくれます。お好みでどうぞ。


5. zipalign が効いているかを確認する
以下のコマンドで確認できます。
$ zipalign -c -v 4 sample.signed-aligned.apk
これもまた、成功したかどうかはフィーリングで分かると思います。

こんな感じです

pom.xml を書くのはやっぱり面倒ですけど、まあ基本的には簡単ですね!