Closed Bug 562843 Opened 14 years ago Closed 14 years ago

Android release signing

Categories

(Release Engineering :: General, defect, P3)

ARM
Android
defect

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: mozilla, Assigned: mozilla)

References

Details

(Whiteboard: [android][automation])

Attachments

(1 file)

After we figure out how to sign Android nightlies, we need a solution for release signing.
Blocks: 561901
Whiteboard: [android][automation]
Hm,

http://developer.android.com/guide/publishing/app-signing.html#overview :

"The certificate does not need to be signed by a certificate authority: it is perfectly allowable, and typical, for Android applications to use self-signed certificates."

http://www.devx.com/wireless/Article/39972/1954 :

"All Android applications must be signed before they are allowed to be deployed onto a device (or emulator). Unlike other mobile platforms, you need not purchase digital certificates from a certificate authority (CA). Instead, you can generate your own personal certificate and use it to sign your Android applications."

From further investigation, it seems like they're actively *discouraging* you from purchasing a CA signed cert.
Bear: do you think this would work?  I'm not seeing anything on verisign.com that specifies Android other than a VIP Access mobile client.

http://www.verisign.com/code-signing/content-signing-certificates/sun-java/index.html?sl=productdetails
Assignee: nobody → aki
Contacted Verisign via web form; waiting for a response.
blocking2.0: --- → ?
Spoke to Verisign otp today; got verbal recommendations that make sense.
They will send me some emails tonight; I will follow up tomorrow.

We're tossing the idea of somehow combining our Authenticode and Java signing certs, which would be theoretically possible but not recommended and not Verisign-supported.  We will purchase a Java signing cert specifically for Android releases.
Status: NEW → ASSIGNED
Trying to get an expire/renew scenario tested, having issues with that.
X.509 validity seems to be what we're looking at.

keytool -keyclone doesn't seem to support changing the validity period. The docs I've found for "renewing" an X.509 cert seem to all involve revoking and re-creating the cert.

http://en.wikipedia.org/wiki/X.509#Sample_X.509_certificates seems to suggest that one could decode the cert, edit the text to change the Validity Not Before: and Not After: lines, and re-encode (self-sign? Verisign-sign?) but this seems like a very fiddly process and I'd want to see it successfully install over a build signed with the previous, expired cert.

Still digging.
Sent an email to Verisign to see if they want to help out; if we're not able to get an expire/renew scenario tested (and successfully install a new build signed with the renewed key on top of an old build signed with the old key) then something like a 25-year self-signed cert would seem safer.
I don't think i've ever successfully done the "fiddly" method by editing the Before/Not After bits and re-encoding.  The method used in the past in my experience has been long duration certs and when you do come up on an expiration you sign the new cert with the same key as the expired cert and list it in the trust chain.  But that also never really was for anything as high profile as Mozilla's products, so yea, let's see what Verisign recommends.

25 years always seems far away until it suddenly isn't ;)
Aha.

# Generate new 2048 bit RSA key, and a Certificate Signing Request for the CA
openssl req -new -newkey rsa:2048 -keyout server.key -out server.csr

# Examine the csr
openssl req -in server.csr -noout -text

# Create a self-signed x.509 cert that expires in 30 days from CSR
openssl x509 -req -days 30 -in server.csr -signkey server.key -out server.cert

# Examine cert
openssl x509 -in server.cert -noout -text

# Create a second self-signed x.509 cert that expires in 365 days from same CSR
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server2.cert

# Examine 2nd cert
openssl x509 -in server2.cert -noout -text

Next: import this into a keystore.  If this isn't easily doable, I can generate the CSR from |keytool -certreq| instead.

After that: sign 2 Android builds with 2 different certs signed with the same private key. Test installing one on top of the other without uninstalling.
keytool -importcert -keystore test2.keystore -file openssl/server.cert -alias cert1

worked.
Not sure if that means I can sign something with this keystore or not; testing that now.
When I try to sign using this keystore, I get:

jarsigner: Certificate chain not found for: cert1.  cert1 must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.

I'm guessing this is because I didn't import the key; found some instructions on how to do that here: http://www.agentbob.info/agentbob/79-AB.html .

I think I'm going to try generating the key and csr in keytool itself first.
How to export the private key from the keystore:
http://conshell.net/wiki/index.php/Keytool_to_OpenSSL_Conversion_tips
Looks like both approaches (importing a key into keystore, exporting a key from keystore) require java.
When running hte javac command in http://conshell.net/wiki/index.php/Keytool_to_OpenSSL_Conversion_tips , I had to remove the line in Base64Coder.java that says "package biz.source_code.base64Coder;" to get it to compile.

Then I ran into the all-on-one-line issue (
 $ openssl pkcs8 -inform PEM -nocrypt -in foo-pkcs8.key -out foo.key
 Error decrypting key
 26898:error:0906D064:PEM routines:PEM_read_bio:bad base64 decode:pem_lib.c:759:
)
which I fixed by opening the pkcs8.key in a text editor and adding newlines so no line was >64chars.

I then generated a csr via keytool -certreq, created a cert from the private key, imported that cert via keytool -importcert, and I'm getting the exact same error as comment 9.

I'm guessing I'm importing wrong.
Aha: http://forums.sun.com/thread.jspa?threadID=5281615 tells me I need to import the cert with the same alias.

You can tell when you keytool -keystore KEYSTORENAME -list, a cert imported with a new alias will show up as a "trustedCertEntry"; a cert created via keytool (or imported over a cert created via keytool) will show up as "PrivateKeyEntry".

Signing with that cert worked. [!]

If I generate the private key outside of keytool, none of the certs show up as "PrivateKeyEntry" -- guessing I need to do something else to import the key, or maybe that's not supported.  Either way, this is progress.
I signed two apks, one with a cert that was going to expire in 30 days, the second with a cert (signed with the same key) that was going to expire in 60 days.

Bear attempted to install the second on top of the first, (adb install -r), and got "INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES" which is here:

http://www.google.com/codesearch/p?hl=en#5oTG8Wvrixk/trunk/android-x86/frameworks/base/core/java/android/content/pm/PackageParser.java&q=INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES&sa=N&cd=2&ct=rc&l=406

After examining that chunk of code, and disclaiming that I'm not a java programmer, it looks very much like there is no support for cert renewal.
Hm, http://developer.android.com/guide/publishing/app-signing.html#cert now says (or I've just now noticed):

"[Your cert must have] a validity period that exceeds the expected lifespan of the application or application suite. A validity period of more than 25 years is recommended.

If you plan to publish your application(s) on Android Market, note that a validity period ending after 22 October 2033 is a requirement. You can not upload an application if it is signed with a key whose validity expires before that date."

I had read the first paragraph, but the second is new to me.
However, we've already come to the conclusion that a long-lived (>23 years, apparently) self-signed cert is the way we need to go.
This is to avoid the passphrases from living in cleartext on disk or on screen.
Attachment #468114 - Flags: review?(bear)
Despite my misgivings, the win32 signing box may be our best option.

SDK here: http://developer.android.com/sdk/index.html
JDK here: http://www.oracle.com/technetwork/java/javase/downloads/index.html

Putting the jdk bin + the sdk tools directories in the user PATH works... ran some tests on my win32 Fusion vm.
This works for me in a cygwin bash script with a patched mozpass.py... it asks for the passphrases interactively.

export PATH=$PATH:/cygdrive/c/Program\ Files/Java/jdk1.6.0_21/bin:/cygdrive/c/an
droid/android-sdk-windows/tools
export JAVA_HOME=/cygdrive/c/Program\ Files/Java/jdk1.6.0_21

cp gecko-unsigned-unaligned.apk gecko-unaligned.apk
#python tools/release/signing/mozpass.py -i --keystore mykeystore.keystore --ali
as myalias --apk gecko-unaligned.apk
python mozpass.py -i --keystore mykeystore.keystore --alias myalias --apk gecko-
unaligned.apk
zipalign -f -v 4 gecko-unaligned.apk fennec.apk
Comment on attachment 468114 [details] [diff] [review]
allow for interactive mozpass mode

I have two questions tho - 1) you got rid of the doctest-if-no-args passed, could you not have kept it but added a --test-code option?

2) now that interactive mode introduces the need to parse the command line, we could make parseConfig() return a dictionary to pass to the code that is used to populate the OptionParser's default values.  This would remove the need for Template
Attachment #468114 - Flags: review?(bear) → review+
(In reply to comment #19)
> I have two questions tho - 1) you got rid of the doctest-if-no-args passed,
> could you not have kept it but added a --test-code option?

I thought about that.
a) we have a --test-only mode that spits out the command line as parsed
b) we have dozens and dozens of working config files to use as samples if needed

Do you still need this?

That does remind me -- I should probably comment out the --test-only option for production runtime.

> 2) now that interactive mode introduces the need to parse the command line, we
> could make parseConfig() return a dictionary to pass to the code that is used
> to populate the OptionParser's default values.  This would remove the need for
> Template

Sure.
I wanted to get the minimal amount of changes in to get interactive mode in time for today, without affecting nightly signing logic at all.
(In reply to comment #20)
> (In reply to comment #19)
> > I have two questions tho - 1) you got rid of the doctest-if-no-args passed,
> > could you not have kept it but added a --test-code option?
> 
> I thought about that.
> a) we have a --test-only mode that spits out the command line as parsed
> b) we have dozens and dozens of working config files to use as samples if
> needed
> 
> Do you still need this?

the doctest was testing the code's internal interfaces - not the functional testing from the outside.  but if the --test-only is excercing the same code-paths then it's the same behaviour. just did not want to lose test coverage even for such a simple tool.
> 
> That does remind me -- I should probably comment out the --test-only option for
> production runtime.
> 
> > 2) now that interactive mode introduces the need to parse the command line, we
> > could make parseConfig() return a dictionary to pass to the code that is used
> > to populate the OptionParser's default values.  This would remove the need for
> > Template
> 
> Sure.
> I wanted to get the minimal amount of changes in to get interactive mode in
> time for today, without affecting nightly signing logic at all.

yea, that's why I r+'d it with comment
Comment on attachment 468114 [details] [diff] [review]
allow for interactive mozpass mode

http://hg.mozilla.org/build/tools/rev/0c0f5f87afa0

I checked this in with the echo bit commented out; got a little paranoid that someone could use that in a not-good way.
Attachment #468114 - Flags: checked-in+
This appears to be resolved.
Documentation is up for the build team. It can definitely become more uniform with Firefox desktop signing, but we have Android release signing.
Status: ASSIGNED → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
blocking2.0: ? → ---
Product: mozilla.org → Release Engineering
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: