README

APK Signing
===========

.. sectnum::
.. contents:: Table of Contents

APK is Android's application packaging format. It's basically a ZIP file that
follows the `JAR Signing`_ specification, and stores a manifest of all file checksums
that gets signed by a private RSA key.

.. _`JAR Signing`: http://download.java.net/jdk7/archive/b125/docs/technotes/tools/solaris/jarsigner.html

Android supports another signing mechanisms that puts a signature in the
metadata of the zip file, but this isn't supported by autograph at this time.


Configuration
-------------

To generate a key and certificate using the standard `keytool` approach, use the
following command:

.. code:: bash

    keytool -keystore testkeystore.jks -genkey -alias testapp -keysize 2048 -keyalg RSA -validity 10000 -keypass password1 -storepass password1

This will create a file called `testkeystore.jks` that contains both the private
RSA key and the public certificate. To export these in PEM format and load them
into the Autograph configuration, we first need to export the keystore into
PKCS12, then extract the private key from the PKCS12 file, as follows:

.. code:: bash

    # export the keystore into pkcs12
    keytool -importkeystore -srckeystore testkeystore.jks -destkeystore testkeystore.p12 -deststoretype PKCS12 -srcalias testapp -deststorepass password1 -destkeypass password1

    # export the private key from the pkcs12 file into PEM
    openssl pkcs12 -in testkeystore.p12  -nodes -nocerts -out key.pem

    # export the public certificate from the keystore into PEM
    keytool -exportcert -keystore testkeystore.jks -alias testapp|openssl x509 -inform der -text

You can then place the certificate and private key in `autograph.yaml`:

.. code:: yaml

	signers:
    - id: some-android-app
      type: apk
      certificate: |
          -----BEGIN CERTIFICATE-----
          MIIH0zCCBbugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBvDELMAkGA1UEBhMCVVMx
		  ...
          -----END CERTIFICATE-----
      privatekey: |
          -----BEGIN PRIVATE KEY-----
          MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDHV+bKFLr1p5FR
		  ...
          -----END PRIVATE KEY-----

Signing legacy APKs
~~~~~~~~~~~~~~~~~~~

For very old APKs (eg. Firefox Fennec for Android), you need to set the parameter `nopkcs7signedattributes: true` in the signer configuration to tell autograph to sign without PKCS7 signed attributes.

Signature request
-----------------

This signer supports both `/sign/data/` and `/sign/file` endpoints.

The `/sign/data` endpoint only does the signing step. It takes a
jarsigner signature file as input and returns a PKCS7 detached
signature. The caller is then responsible for repacking the APK. It
uses the request format:

.. code:: json

	[
		{
			"input": "Y2FyaWJvdW1hdXJpY2UK",
			"keyid": "some-android-app"
		}
	]

**NB: Signing an APK with autograph-edge will require setting the zip option in its config. Ask the autograph ops to do that for you.** 

The `/sign/file` endpoint takes a whole APK encoded in base64. It will
unzip the apk, generate the manifests, sign and align the output
zip. It uses the same request format with an optional param `zip` that
defaults to `"all"` for DEFLATE compressing all files in the APK. It
can also be `"passthrough"` to preserve the compression of the input
APK (e.g. for `mmap`ed media files) in which case the caller is
responsible for zip-aligning APK after submitting it:

.. code:: json

	[
		{
			"input": "Y2FyaWJvdW1hdXJpY2UK",
			"keyid": "some-android-app",
			"options": {
				"zip": "all"
			}
		}
	]

Both endpoints take an **optional** string representing a supported
PKCS7 digest algorithm (`"SHA1"`, `"SHA256"`, `"SHA384"` or `"SHA512"`).
Both `/sign/file` and `/sign/data` support this option. It defaults to SHA256
for null and the empty `""` string.


.. code:: json

	[
		{
			"input": "Y2FyaWJvdW1hdXJpY2UK",
			"keyid": "some-android-app",
			"options": {
				"zip": "all",
				"pkcs7_digest": "SHA1"
			}
		}
	]
	
Signature response
------------------

Data Signing
~~~~~~~~~~~~

The response to a data signing request contains the base64 of the PKCS7 detached
signature in the `signature` field of the JSON response. You should decode this
base64 and write it to a file called `META-INF/SIGNATURE.RSA` in the APK.

.. code:: json

	[
	  {
	    "ref": "7khgpu4gcfdv30w8joqxjy1cc",
	    "type": "apk",
	    "signer_id": "testapp-android",
	    "signature": "MIIGPQYJKoZIhvcN..."
	  }
	]


The response to a file signing request contains the base64 of the signed and
aligned APK in the `signed_file` field of the json response. You should base64
decode that field and write the output as a file.

.. code:: json

	[
	  {
	    "ref": "7khgpu4gcfdv30w8joqxjy1cc",
	    "type": "apk",
	    "signer_id": "testapp-android",
	    "signed_file": "MIIGPQYJKoZIhvcN..."
	  }
	]

Verifying signatures
--------------------

The android SDK has a tool called `apksigner` that can verify both signature
versions, as well as the zip alignment.

.. code:: bash

	$ /opt/android-sdk/build-tools/27.0.3/apksigner verify -v test.apk

	Verifies
	Verified using v1 scheme (JAR signing): true
	Verified using v2 scheme (APK Signature Scheme v2): false
	Number of signers: 1

Documentation

Index

Constants

View Source
const (
	// Type of this signer is "apk"
	Type = "apk"

	// ZIPMethodCompressAll represents the default option to
	// compress all files in repacked JARs
	ZIPMethodCompressAll = "all"
	// ZIPMethodCompressPassthrough represents the option to pass
	// through all files with their current compression in JARs
	ZIPMethodCompressPassthrough = "passthrough"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type APKSigner

type APKSigner struct {
	signer.Configuration
	// contains filtered or unexported fields
}

    An APKSigner is configured to issue PKCS7 detached signatures for Android application packages.

    func New

    func New(conf signer.Configuration) (s *APKSigner, err error)

      New initializes an apk signer using a configuration

      func (*APKSigner) Config

      func (s *APKSigner) Config() signer.Configuration

        Config returns the configuration of the current signer

        func (*APKSigner) GetDefaultOptions

        func (s *APKSigner) GetDefaultOptions() interface{}

          GetDefaultOptions returns default options of the signer

          func (*APKSigner) SignData

          func (s *APKSigner) SignData(sigfile []byte, options interface{}) (signer.Signature, error)

            SignData takes a JAR signature file and returns a pkcs7 signature

            func (*APKSigner) SignFile

            func (s *APKSigner) SignFile(input []byte, options interface{}) (signer.SignedFile, error)

              SignFile takes an unsigned, unaligned APK in base64 form, generates the JAR manifests and calls SignData to sign the signature file using the configured private key.

              The PKCS7 detached signature and the manifests are then stored inside the ZIP file under META-INF, and the whole archived is aligned. The returned data is the signed aligned APK.

              This implements apksigning v1, aka jarsigner. apksigning v2 is not supported.

              type Options

              type Options struct {
              	ZIP string `json:"zip"`
              
              	// PKCS7Digest is a string referring to algorithm to use for the PKCS7 signature digest
              	PKCS7Digest string `json:"pkcs7_digest"`
              }

                Options is empty for this signer type

                func GetOptions

                func GetOptions(input interface{}) (options Options, err error)

                  GetOptions takes a input interface and reflects it into a struct of options

                  func (*Options) PK7Digest

                  func (o *Options) PK7Digest() (asn1.ObjectIdentifier, error)

                    PK7Digest validates and return an ASN OID for a PKCS7 digest algorithm or an error. If no specific digest is specific, SHA256 is used.

                    type Signature

                    type Signature struct {
                    	Data     []byte
                    	Finished bool
                    	// contains filtered or unexported fields
                    }

                      Signature is a PKCS7 detached signature

                      func Unmarshal

                      func Unmarshal(signature string, content []byte) (sig *Signature, err error)

                        Unmarshal takes the base64 representation of a PKCS7 detached signature and the content of the signed data, and returns a PKCS7 struct

                        func (*Signature) Marshal

                        func (sig *Signature) Marshal() (string, error)

                          Marshal returns the base64 representation of a PKCS7 detached signature

                          func (*Signature) String

                          func (sig *Signature) String() string

                            String returns a PEM encoded PKCS7 block

                            func (*Signature) Verify

                            func (sig *Signature) Verify() error

                              Verify verifies an apk pkcs7 signature WARNING: this function does not verify the JAR manifests

                              Source Files