writer.go 6.8 KB

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