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 }