SignApk.java 源码
16lz
2021-01-26
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.signapk;import sun.misc.BASE64Encoder;import sun.security.pkcs.ContentInfo;import sun.security.pkcs.PKCS7;import sun.security.pkcs.SignerInfo;import sun.security.x509.AlgorithmId;import sun.security.x509.X500Name;import java.io.BufferedReader;import java.io.ByteArrayOutputStream;import java.io.DataInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.FilterOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintStream;import java.security.AlgorithmParameters;import java.security.DigestOutputStream;import java.security.GeneralSecurityException;import java.security.Key;import java.security.KeyFactory;import java.security.MessageDigest;import java.security.PrivateKey;import java.security.Signature;import java.security.SignatureException;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import java.security.spec.InvalidKeySpecException;import java.security.spec.KeySpec;import java.security.spec.PKCS8EncodedKeySpec;import java.util.ArrayList;import java.util.Collections;import java.util.Date;import java.util.Enumeration;import java.util.List;import java.util.Map;import java.util.TreeMap;import java.util.jar.Attributes;import java.util.jar.JarEntry;import java.util.jar.JarFile;import java.util.jar.JarOutputStream;import java.util.jar.Manifest;import java.util.regex.Pattern;import javax.crypto.Cipher;import javax.crypto.EncryptedPrivateKeyInfo;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.PBEKeySpec;/** * Command line tool to sign JAR files (including APKs and OTA updates) in * a way compatible with the mincrypt verifier, using SHA1 and RSA keys. */class SignApk { private static final String CERT_SF_NAME = "META-INF/CERT.SF"; private static final String CERT_RSA_NAME = "META-INF/CERT.RSA"; // Files matching this pattern are not copied to the output. private static Pattern stripPattern = Pattern.compile("^META-INF/(.*)[.](SF|RSA|DSA)$"); private static X509Certificate readPublicKey(File file) throws IOException, GeneralSecurityException { FileInputStream input = new FileInputStream(file); try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); return (X509Certificate) cf.generateCertificate(input); } finally { input.close(); } } /** * Reads the password from stdin and returns it as a string. * * @param keyFile The file containing the private key. Used to prompt the user. */ private static String readPassword(File keyFile) { // TODO: use Console.readPassword() when it's available. System.out.print("Enter password for " + keyFile + " (password will not be hidden): "); System.out.flush(); BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); try { return stdin.readLine(); } catch (IOException ex) { return null; } } /** * Decrypt an encrypted PKCS 8 format private key. * * Based on ghstark's post on Aug 6, 2006 at * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949 * * @param encryptedPrivateKey The raw data of the private key * @param keyFile The file containing the private key */ private static KeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile) throws GeneralSecurityException { EncryptedPrivateKeyInfo epkInfo; try { epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey); } catch (IOException ex) { // Probably not an encrypted key. return null; } char[] password = readPassword(keyFile).toCharArray(); SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName()); Key key = skFactory.generateSecret(new PBEKeySpec(password)); Cipher cipher = Cipher.getInstance(epkInfo.getAlgName()); cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters()); try { return epkInfo.getKeySpec(cipher); } catch (InvalidKeySpecException ex) { System.err.println("signapk: Password for " + keyFile + " may be bad."); throw ex; } } /** Read a PKCS 8 format private key. */ private static PrivateKey readPrivateKey(File file) throws IOException, GeneralSecurityException { DataInputStream input = new DataInputStream(new FileInputStream(file)); try { byte[] bytes = new byte[(int) file.length()]; input.read(bytes); KeySpec spec = decryptPrivateKey(bytes, file); if (spec == null) { spec = new PKCS8EncodedKeySpec(bytes); } try { return KeyFactory.getInstance("RSA").generatePrivate(spec); } catch (InvalidKeySpecException ex) { return KeyFactory.getInstance("DSA").generatePrivate(spec); } } finally { input.close(); } } /** Add the SHA1 of every file to the manifest, creating it if necessary. */ private static Manifest addDigestsToManifest(JarFile jar) throws IOException, GeneralSecurityException { Manifest input = jar.getManifest(); Manifest output = new Manifest(); Attributes main = output.getMainAttributes(); if (input != null) { main.putAll(input.getMainAttributes()); } else { main.putValue("Manifest-Version", "1.0"); main.putValue("Created-By", "1.0 (Android SignApk)"); } BASE64Encoder base64 = new BASE64Encoder(); MessageDigest md = MessageDigest.getInstance("SHA1"); byte[] buffer = new byte[4096]; int num; // We sort the input entries by name, and add them to the // output manifest in sorted order. We expect that the output // map will be deterministic. TreeMap byName = new TreeMap(); for (Enumeration e = jar.entries(); e.hasMoreElements(); ) { JarEntry entry = e.nextElement(); byName.put(entry.getName(), entry); } for (JarEntry entry: byName.values()) { String name = entry.getName(); if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && (stripPattern == null || !stripPattern.matcher(name).matches())) { InputStream data = jar.getInputStream(entry); while ((num = data.read(buffer)) > 0) { md.update(buffer, 0, num); } Attributes attr = null; if (input != null) attr = input.getAttributes(name); attr = attr != null ? new Attributes(attr) : new Attributes(); attr.putValue("SHA1-Digest", base64.encode(md.digest())); output.getEntries().put(name, attr); } } return output; } /** Write to another stream and also feed it to the Signature object. */ private static class SignatureOutputStream extends FilterOutputStream { private Signature mSignature; private int mCount; public SignatureOutputStream(OutputStream out, Signature sig) { super(out); mSignature = sig; mCount = 0; } @Override public void write(int b) throws IOException { try { mSignature.update((byte) b); } catch (SignatureException e) { throw new IOException("SignatureException: " + e); } super.write(b); mCount++; } @Override public void write(byte[] b, int off, int len) throws IOException { try { mSignature.update(b, off, len); } catch (SignatureException e) { throw new IOException("SignatureException: " + e); } super.write(b, off, len); mCount += len; } public int size() { return mCount; } } /** Write a .SF file with a digest of the specified manifest. */ private static void writeSignatureFile(Manifest manifest, SignatureOutputStream out) throws IOException, GeneralSecurityException { Manifest sf = new Manifest(); Attributes main = sf.getMainAttributes(); main.putValue("Signature-Version", "1.0"); main.putValue("Created-By", "1.0 (Android SignApk)"); BASE64Encoder base64 = new BASE64Encoder(); MessageDigest md = MessageDigest.getInstance("SHA1"); PrintStream print = new PrintStream( new DigestOutputStream(new ByteArrayOutputStream(), md), true, "UTF-8"); // Digest of the entire manifest manifest.write(print); print.flush(); main.putValue("SHA1-Digest-Manifest", base64.encode(md.digest())); Map entries = manifest.getEntries(); for (Map.Entry entry : entries.entrySet()) { // Digest of the manifest stanza for this entry. print.print("Name: " + entry.getKey() + "\r\n"); for (Map.Entry
更多相关文章
- 代码中设置drawableleft
- android 3.0 隐藏 系统标题栏
- Android开发中activity切换动画的实现
- Android(安卓)学习 笔记_05. 文件下载
- Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
- 技术博客汇总
- android 2.3 wifi (一)
- AndRoid Notification的清空和修改
- Android中的Chronometer