37

Using Ant, I'm trying to build an Android application in release mode for distribution. My problem is at the signing process. I've created a keystore and alias via Eclipse using the Export Android Application wizard and the app is correctly signed if export it via Eclipse. When I try to complete the same process via Ant I reference my keystore and alias in my build.properties file:

key.store=C:\\Users\\a512091\\.android\\release.keystore
key.alias=application
key.store.password=android
key.alias.password=android

The build process is successful and I get an Application-release.apk file. I veryfied this APK with jarsigner and all files have "sm" tags. This is the tail of the output:

jar verified.
Warning:
This jar contains entries whose certificate chain is not validated.

When I try to install this APK into an emulator or device I get the following:

Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]

Logcat shows signing problems on my CSS file and image assets:

11-07 11:06:20.060: WARN/PackageParser(58): Exception reading assets/www/css/base.css in /data/app/vmdl48898.tmp
11-07 11:06:20.060: WARN/PackageParser(58): java.lang.SecurityException: META-INF/XXXXX.SF has invalid digest for assets/www/res/droidhdpi/favorite_off.png in /data/app/vmdl48898.tmp
11-07 11:06:20.060: WARN/PackageParser(58):     at java.util.jar.JarVerifier.verifyCertificate(JarVerifier.java:369)
11-07 11:06:20.060: WARN/PackageParser(58):     at java.util.jar.JarVerifier.readCertificates(JarVerifier.java:272)
11-07 11:06:20.060: WARN/PackageParser(58):     at java.util.jar.JarFile.getInputStream(JarFile.java:392)
11-07 11:06:20.060: WARN/PackageParser(58):     at android.content.pm.PackageParser.loadCertificates(PackageParser.java:337)
11-07 11:06:20.060: WARN/PackageParser(58):     at android.content.pm.PackageParser.collectCertificates(PackageParser.java:508)
11-07 11:06:20.060: WARN/PackageParser(58):     at com.android.server.PackageManagerService.installPackageLI(PackageManagerService.java:5885)
11-07 11:06:20.060: WARN/PackageParser(58):     at com.android.server.PackageManagerService.access$2100(PackageManagerService.java:134)
11-07 11:06:20.060: WARN/PackageParser(58):     at com.android.server.PackageManagerService$5.run(PackageManagerService.java:4743)
11-07 11:06:20.060: WARN/PackageParser(58):     at android.os.Handler.handleCallback(Handler.java:587)
11-07 11:06:20.060: WARN/PackageParser(58):     at android.os.Handler.dispatchMessage(Handler.java:92)
11-07 11:06:20.060: WARN/PackageParser(58):     at android.os.Looper.loop(Looper.java:123)
11-07 11:06:20.060: WARN/PackageParser(58):     at android.os.HandlerThread.run(HandlerThread.java:60)
11-07 11:06:20.069: ERROR/PackageParser(58): Package com.xxxxx.xxxxx has no certificates at entry assets/www/css/base.css; ignoring!
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sgimeno
  • 1,883
  • 1
  • 22
  • 35

6 Answers6

49

If you have Ant version < 1.8.3 (ant -version) try this approach for the issue with JDK 7 (based on the previous answer):

  1. Add signjarjdk7 to ANDROID_SDK\tools\ant\build.xml

    <macrodef name="signjarjdk7">
        <attribute name="jar" />
        <attribute name="signedjar" />
        <attribute name="keystore" />
        <attribute name="storepass" />
        <attribute name="alias" />
        <attribute name="keypass" />
        <attribute name="verbose" />
        <sequential>
            <exec executable="jarsigner" failonerror="true">
                <!-- Magic key, always verbose -->
                <arg line="-verbose -digestalg SHA1 -sigalg MD5withRSA" />
                <arg line="-keystore @{keystore} -storepass @{storepass} -keypass @{keypass}" />
                <arg line="-signedjar &quot;@{signedjar}&quot;" />
                <arg line="&quot;@{jar}&quot; @{alias}" />
            </exec>
        </sequential>
    </macrodef>
    
  2. Replace 'signjar' to 'signjarjdk7' in 'release' target in the same build.xml.

NOTE: You have to define 'key.store.password' and 'key.alias.password' propeties for your project (in project.properties or in local.properties).

UPDATE 1:

If your have installed Ant 1.8.3 (or later) you have a better solution:

Open your ANDROID_SDK\tools\ant\build.xml and add two new parameters - sigalg and digestalg - in the original 'signjar' invocation:

<signjar
    sigalg="MD5withRSA"
    digestalg="SHA1"
    jar="${out.packaged.file}"
    signedjar="${out.unaligned.file}"
    keystore="${key.store}"
    storepass="${key.store.password}"
    alias="${key.alias}"
    keypass="${key.alias.password}"
    verbose="${verbose}" />

UPDATE 2: It seems this answer is deprecated after 'signjar' was replaced to 'signapk' in latest version of Android SDK tools.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
FeelGood
  • 5,594
  • 2
  • 25
  • 22
  • How should the `verbose` attribute be taken into account? Now the verbose flag is always set. – Juuso Ohtonen Mar 09 '12 at 10:57
  • "verbose" needs only to easy replace original 'signjar' target. – FeelGood Apr 08 '12 at 09:59
  • Can you give more information for your 1.8.3+ solution. What are those variables? Where do I get their values from? – Guy Oct 16 '12 at 12:19
  • @Guy, you should update existing 'signjar' target in standart ANDROID_SDK\tools\ant\build.xml. This values are initilized internally during 'ant debug' or 'ant release'. I have updated my answer. – FeelGood Oct 17 '12 at 17:05
  • I am using ant-1.8.4 but in build.xml i don't find 'signjar' target.where should i add this? and from where should i call this target? – Ads Feb 06 '13 at 10:14
  • Ads, you should update existing 'signjar' target in standart ANDROID_SDK\tools\ant\build.xml. It is a part of android-sdk. – FeelGood Feb 07 '13 at 11:50
  • In latest (21.1) android SDK I see 'signapk' instead of 'signjar' target. Maybe it works correctly with JDK 7 (I can not check, b/c use 1.6). – FeelGood Feb 27 '13 at 17:15
  • @FeelGood It doesn't work correctly... I've been searching for a solution for hours but all info I find is related to signjar and not signapk. – devius Dec 18 '13 at 21:45
  • @devius, you are right. The answer is a little bit deprecated and in new sdk versions ant target named signapk instead of signjar. You need to put the same extra paramenters to that target. – FeelGood Dec 23 '13 at 09:33
  • I seem to be getting the following build error when running ant: "signapk doesn't support the "sigalg" attribute". I'm running OpenJDK 1.7.0_51. What could be the problem? – Paul Lammertsma Jan 30 '14 at 14:28
  • Using the macrodef as described above works with OpenJDK 1.7. Note that if you get a segmentation fault (SIGBUS in libzip.so), it may be because the source file is missing. – Paul Lammertsma Jan 30 '14 at 15:23
  • This is all very confusing to me because at http://developer.android.com/tools/publishing/app-signing.html#signapp they say to use `-sigalg SHA1withRSA -digestalg SHA1`. Why are we using MD5withRSA? – Bruno Bronosky Jun 04 '15 at 20:26
  • Bruno, I think it should work with SHA1withRSA too. Try and be free to edit answer. – FeelGood Jun 10 '15 at 08:57
10

It sounds as you may be using JDK 7 (1.7.0) so try adding these options when signing with jarsigner:

-digestalg SHA1 -sigalg MD5withRSA
mr-euro
  • 2,732
  • 5
  • 23
  • 27
  • Yes I'm using JDK 7 so I tried your options and sign process finished perfectly. Thank you! I assume this arguments are also compatible with JDK 6, am I right? – sgimeno Nov 08 '11 at 12:09
  • The bad thing is the signjar task in ANT doesn't support an 'args' attribute so signature has to be done with an exec or java task wich is less clean I think. – sgimeno Nov 08 '11 at 13:09
  • Yes, those arguments were also present in JDK 6 I reckon. – mr-euro Nov 08 '11 at 16:42
  • Sorry for the duplicate comment. Trying to increase my odds of getting a reply. This is all very confusing to me because at http://developer.android.com/tools/publishing/app-signing.html#signapp they say to use `-sigalg SHA1withRSA -digestalg SHA1`. Why are we using MD5withRSA? – Bruno Bronosky Jun 04 '15 at 20:27
6

Per Android developer documentation, you should put these properties within the ant.properties file:

$ cat ant.properties
key.store=C:\\Users\\a512091\\.android\\release.keystore
key.alias=application
key.store.password=android
key.alias.password=android
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Plinio.Santos
  • 1,709
  • 24
  • 31
2

The long term solution is to patch Ant's signjar task:

https://issues.apache.org/bugzilla/show_bug.cgi?id=52344

The new attributes were added to signjar in Ant 1.8.3, but Android's build script (as of r19) has not yet been modified to utilize them:

http://code.google.com/p/android/issues/detail?id=19567

In the meantime, "presetdef" may provide a workaround:

 <presetdef name="signjar">
  <signjar sigalg="MD5withRSA" digestalg="SHA1" />
 </presetdef>
Joe Bowbeer
  • 3,574
  • 3
  • 36
  • 47
  • 1
    Sorry for the duplicate comment. Trying to increase my odds of getting a reply. This is all very confusing to me because at http://developer.android.com/tools/publishing/app-signing.html#signapp they say to use `-sigalg SHA1withRSA -digestalg SHA1`. Why are we using MD5withRSA? – Bruno Bronosky Jun 04 '15 at 20:28
  • The workaround -sigalg=MD5withRSA was specifying what was the RSA default for jarsigner in Java 6. This default changed to SHA256withRSA in Java 7, however, this was not supported on Android. SHA1withRSA is a more secure option that is (apparently) supported on Android, and so is preferable. – Joe Bowbeer Jun 04 '15 at 21:51
1

Using Ubuntu 14.04 (Trusty Tahr) and Windows, create a “.keystore” file.

This file needs to be generated, which can be done with the keytool command that comes with Java. It can typically be found at ‘C:\Program Files\Java\jre7\bin’. Which also has to be added to your PATH variable.

Go to the root of your project and use this command:

Generating a .keystore file:

$ keytool -genkey -v -keystore key-name.keystore -alias alias-name -keyalg RSA -keysize 2048 -validity 10000

Create a file called ant.properties in the folder “platforms/android/” ant.properties.

key.store=D:\\path\\to\\the\\project\\keyname.keystore
key.alias=alias-name

Create the build APK file:

$ cordova build android --release
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Conor
  • 494
  • 6
  • 15
0

If you're stuck (like me) with a version of Ant older than 1.8.3 and Java 7, here's a workaround:

<exec executable="${java.bin.path}/jarsigner">
  <arg value="-signedjar"/>
  <arg value="signed-${app.apk.name}"/>
  <arg value="-keystore"/>
  <arg value="my.keystore"/>
  <arg value="-storepass"/>
  <arg value="passwd"/>
  <arg value="-sigalg"/>
  <arg value="MD5withRSA"/>
  <arg value="-digestalg"/>
  <arg value="SHA1"/>
  <arg value="${app.apk.name}"/>
  <arg value="my_keystore"/>
</exec>

<!-- Where old version was: -->

<signjar
  alias="my_keystore" keystore="my.keystore"
  storepass="passwd"
  preservelastmodified="true"
  signedjar="signed-${app.apk.name}">
  <path>
  <fileset dir="." includes="${app.apk.name}" />
  </path>
</signjar>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hemisphire
  • 1,205
  • 9
  • 19