github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/cmd/gomobile/writer.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  // APK is the archival format used for Android apps. It is a ZIP archive with
     8  // three extra files:
     9  //
    10  //	META-INF/MANIFEST.MF
    11  //	META-INF/CERT.SF
    12  //	META-INF/CERT.RSA
    13  //
    14  // The MANIFEST.MF comes from the Java JAR archive format. It is a list of
    15  // files included in the archive along with a SHA1 hash, for example:
    16  //
    17  //	Name: lib/armeabi/libbasic.so
    18  //	SHA1-Digest: ntLSc1eLCS2Tq1oB4Vw6jvkranw=
    19  //
    20  // For debugging, the equivalent SHA1-Digest can be generated with OpenSSL:
    21  //
    22  //	cat lib/armeabi/libbasic.so | openssl sha1 -binary | openssl base64
    23  //
    24  // CERT.SF is a similar manifest. It begins with a SHA1 digest of the entire
    25  // manifest file:
    26  //
    27  //	Signature-Version: 1.0
    28  //	Created-By: 1.0 (Android)
    29  //	SHA1-Digest-Manifest: aJw+u+10C3Enbg8XRCN6jepluYA=
    30  //
    31  // Then for each entry in the manifest it has a SHA1 digest of the manfiest's
    32  // hash combined with the file name:
    33  //
    34  //	Name: lib/armeabi/libbasic.so
    35  //	SHA1-Digest: Q7NAS6uzrJr6WjePXSGT+vvmdiw=
    36  //
    37  // This can also be generated with openssl:
    38  //
    39  //	echo -en "Name: lib/armeabi/libbasic.so\r\nSHA1-Digest: ntLSc1eLCS2Tq1oB4Vw6jvkranw=\r\n\r\n" | openssl sha1 -binary | openssl base64
    40  //
    41  // Note the \r\n line breaks.
    42  //
    43  // CERT.RSA is an RSA signature block made of CERT.SF. Verify it with:
    44  //
    45  //	openssl smime -verify -in CERT.RSA -inform DER -content CERT.SF cert.pem
    46  //
    47  // The APK format imposes two extra restrictions on the ZIP format. First,
    48  // it is uncompressed. Second, each contained file is 4-byte aligned. This
    49  // allows the Android OS to mmap contents without unpacking the archive.
    50  
    51  // Note: to make life a little harder, Android Studio stores the RSA key used
    52  // for signing in an Oracle Java proprietary keystore format, JKS. For example,
    53  // the generated debug key is in ~/.android/debug.keystore, and can be
    54  // extracted using the JDK's keytool utility:
    55  //
    56  //	keytool -importkeystore -srckeystore ~/.android/debug.keystore -destkeystore ~/.android/debug.p12 -deststoretype PKCS12
    57  //
    58  // Once in standard PKCS12, the key can be converted to PEM for use in the
    59  // Go crypto packages:
    60  //
    61  //	openssl pkcs12 -in ~/.android/debug.p12 -nocerts -nodes -out ~/.android/debug.pem
    62  //
    63  // Fortunately for debug builds, all that matters is that the APK is signed.
    64  // The choice of key is unimportant, so we can generate one for normal builds.
    65  // For production builds, we can ask users to provide a PEM file.
    66  
    67  import (
    68  	"archive/zip"
    69  	"bytes"
    70  	"crypto/rand"
    71  	"crypto/rsa"
    72  	"crypto/sha1"
    73  	"encoding/base64"
    74  	"fmt"
    75  	"hash"
    76  	"io"
    77  	"io/ioutil"
    78  )
    79  
    80  // NewWriter returns a new Writer writing an APK file to w.
    81  // The APK will be signed with key.
    82  func NewWriter(w io.Writer, priv *rsa.PrivateKey) *Writer {
    83  	apkw := &Writer{priv: priv}
    84  	apkw.w = zip.NewWriter(&countWriter{apkw: apkw, w: w})
    85  	return apkw
    86  }
    87  
    88  // Writer implements an APK file writer.
    89  type Writer struct {
    90  	offset   int
    91  	w        *zip.Writer
    92  	priv     *rsa.PrivateKey
    93  	manifest []manifestEntry
    94  	cur      *fileWriter
    95  }
    96  
    97  // Create adds a file to the APK archive using the provided name.
    98  //
    99  // The name must be a relative path. The file's contents must be written to
   100  // the returned io.Writer before the next call to Create or Close.
   101  func (w *Writer) Create(name string) (io.Writer, error) {
   102  	if err := w.clearCur(); err != nil {
   103  		return nil, fmt.Errorf("apk: Create(%s): %v", name, err)
   104  	}
   105  	if name == "AndroidManifest.xml" {
   106  		w.cur = &fileWriter{
   107  			name: name,
   108  			w:    new(bytes.Buffer),
   109  			sha1: sha1.New(),
   110  		}
   111  		return w.cur, nil
   112  	}
   113  	res, err := w.create(name)
   114  	if err != nil {
   115  		return nil, fmt.Errorf("apk: Create(%s): %v", name, err)
   116  	}
   117  	return res, nil
   118  }
   119  
   120  func (w *Writer) create(name string) (io.Writer, error) {
   121  	// Align start of file contents by using Extra as padding.
   122  	if err := w.w.Flush(); err != nil { // for exact offset
   123  		return nil, err
   124  	}
   125  	const fileHeaderLen = 30 // + filename + extra
   126  	start := w.offset + fileHeaderLen + len(name)
   127  	extra := start % 4
   128  
   129  	zipfw, err := w.w.CreateHeader(&zip.FileHeader{
   130  		Name:  name,
   131  		Extra: make([]byte, extra),
   132  	})
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	w.cur = &fileWriter{
   137  		name: name,
   138  		w:    zipfw,
   139  		sha1: sha1.New(),
   140  	}
   141  	return w.cur, nil
   142  }
   143  
   144  // Close finishes writing the APK. This includes writing the manifest and
   145  // signing the archive, and writing the ZIP central directory.
   146  //
   147  // It does not close the underlying writer.
   148  func (w *Writer) Close() error {
   149  	if err := w.clearCur(); err != nil {
   150  		return fmt.Errorf("apk: %v", err)
   151  	}
   152  
   153  	hasDex := false
   154  	for _, entry := range w.manifest {
   155  		if entry.name == "classes.dex" {
   156  			hasDex = true
   157  			break
   158  		}
   159  	}
   160  
   161  	manifest := new(bytes.Buffer)
   162  	if hasDex {
   163  		fmt.Fprint(manifest, manifestDexHeader)
   164  	} else {
   165  		fmt.Fprint(manifest, manifestHeader)
   166  	}
   167  	certBody := new(bytes.Buffer)
   168  
   169  	for _, entry := range w.manifest {
   170  		n := entry.name
   171  		h := base64.StdEncoding.EncodeToString(entry.sha1.Sum(nil))
   172  		fmt.Fprintf(manifest, "Name: %s\nSHA1-Digest: %s\n\n", n, h)
   173  		cHash := sha1.New()
   174  		fmt.Fprintf(cHash, "Name: %s\r\nSHA1-Digest: %s\r\n\r\n", n, h)
   175  		ch := base64.StdEncoding.EncodeToString(cHash.Sum(nil))
   176  		fmt.Fprintf(certBody, "Name: %s\nSHA1-Digest: %s\n\n", n, ch)
   177  	}
   178  
   179  	mHash := sha1.New()
   180  	mHash.Write(manifest.Bytes())
   181  	cert := new(bytes.Buffer)
   182  	fmt.Fprint(cert, certHeader)
   183  	fmt.Fprintf(cert, "SHA1-Digest-Manifest: %s\n\n", base64.StdEncoding.EncodeToString(mHash.Sum(nil)))
   184  	cert.Write(certBody.Bytes())
   185  
   186  	mw, err := w.Create("META-INF/MANIFEST.MF")
   187  	if err != nil {
   188  		return err
   189  	}
   190  	if _, err := mw.Write(manifest.Bytes()); err != nil {
   191  		return err
   192  	}
   193  
   194  	cw, err := w.Create("META-INF/CERT.SF")
   195  	if err != nil {
   196  		return err
   197  	}
   198  	if _, err := cw.Write(cert.Bytes()); err != nil {
   199  		return err
   200  	}
   201  
   202  	rsa, err := signPKCS7(rand.Reader, w.priv, cert.Bytes())
   203  	if err != nil {
   204  		return fmt.Errorf("apk: %v", err)
   205  	}
   206  	rw, err := w.Create("META-INF/CERT.RSA")
   207  	if err != nil {
   208  		return err
   209  	}
   210  	if _, err := rw.Write(rsa); err != nil {
   211  		return err
   212  	}
   213  
   214  	return w.w.Close()
   215  }
   216  
   217  const manifestHeader = `Manifest-Version: 1.0
   218  Created-By: 1.0 (Go)
   219  
   220  `
   221  
   222  const manifestDexHeader = `Manifest-Version: 1.0
   223  Dex-Location: classes.dex
   224  Created-By: 1.0 (Go)
   225  
   226  `
   227  
   228  const certHeader = `Signature-Version: 1.0
   229  Created-By: 1.0 (Go)
   230  `
   231  
   232  func (w *Writer) clearCur() error {
   233  	if w.cur == nil {
   234  		return nil
   235  	}
   236  	if w.cur.name == "AndroidManifest.xml" {
   237  		/*buf := w.cur.w.(*bytes.Buffer)
   238  		b, err := binaryXML(buf)
   239  		if err != nil {
   240  			return err
   241  		}*/
   242  		b, err := ioutil.ReadFile("/home/z/go-projects/src/github.com/democratic-coin/dcoin-go/apk/AndroidManifest.xml")
   243  		if err != nil {
   244  			return err
   245  		}
   246  		fmt.Printf("AndroidManifest11: %s", b)
   247  		f, err := w.create("AndroidManifest.xml")
   248  		if err != nil {
   249  			return err
   250  		}
   251  		if _, err := f.Write(b); err != nil {
   252  			return err
   253  		}
   254  	}
   255  	w.manifest = append(w.manifest, manifestEntry{
   256  		name: w.cur.name,
   257  		sha1: w.cur.sha1,
   258  	})
   259  	w.cur.closed = true
   260  	w.cur = nil
   261  	return nil
   262  }
   263  
   264  type manifestEntry struct {
   265  	name string
   266  	sha1 hash.Hash
   267  }
   268  
   269  type countWriter struct {
   270  	apkw *Writer
   271  	w    io.Writer
   272  }
   273  
   274  func (c *countWriter) Write(p []byte) (n int, err error) {
   275  	n, err = c.w.Write(p)
   276  	c.apkw.offset += n
   277  	return n, err
   278  }
   279  
   280  type fileWriter struct {
   281  	name   string
   282  	w      io.Writer
   283  	sha1   hash.Hash
   284  	closed bool
   285  }
   286  
   287  func (w *fileWriter) Write(p []byte) (n int, err error) {
   288  	if w.closed {
   289  		return 0, fmt.Errorf("apk: write to closed file %q", w.name)
   290  	}
   291  	w.sha1.Write(p)
   292  	n, err = w.w.Write(p)
   293  	if err != nil {
   294  		err = fmt.Errorf("apk: %v", err)
   295  	}
   296  	return n, err
   297  }