Android数字签名解析(二)

在Android数字签名解析(一)中,介绍了android进行签名的两种方式,当中用密钥对进行签名用到了signapk.jar这个java库。

以下我们就看看signapk签名实现过程,signapk的源代码在build/tools/signapk/下。

一、生成MANIFEST.MF文件

      //对apk包中的每一个文件(非目录和非签名文件),生成SHA1的摘要信息。再对这个信息进行Base64编码。
      Manifest manifest = addDigestsToManifest(inputJar);
      //将上面得到的信息。写入MANIFEST.MF
      je = new JarEntry(JarFile.MANIFEST_NAME);
      je.setTime(timestamp);
      outputJar.putNextEntry(je);
      manifest.write(outputJar);

二、 生成CERT.SF文件

      je = new JarEntry(CERT_SF_NAME);
      je.setTime(timestamp);
      outputJar.putNextEntry(je);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      writeSignatureFile(manifest, baos);
      byte[] signedData = baos.toByteArray();
      outputJar.write(signedData);

对 整个 MANIFEST.MF 进行 SHA1 计算。并将摘要信息存入 CERT.SF 中 。然后对之前计算的全部摘要信息使用SHA1再次计

算,将结果也写入 CERT.SF 中, 关键代码在 writeSignatureFile(manifest,
baos)中,

   /** Write a .SF file with a digest of the specified manifest. */
    private static void writeSignatureFile(Manifest manifest, OutputStream 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)");

        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",
                      new String(Base64.encode(md.digest()), "ASCII"));

        Map<String, Attributes> entries = manifest.getEntries();
        for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
            // Digest of the manifest stanza for this entry.
            print.print("Name: " + entry.getKey() + "\r\n");
            for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
                print.print(att.getKey() + ": " + att.getValue() + "\r\n");
            }
            print.print("\r\n");
            print.flush();

            Attributes sfAttr = new Attributes();
            sfAttr.putValue("SHA1-Digest",
                            new String(Base64.encode(md.digest()), "ASCII"));
            sf.getEntries().put(entry.getKey(), sfAttr);
        }

        CountOutputStream cout = new CountOutputStream(out);
        sf.write(cout);

        // A bug in the java.util.jar implementation of Android platforms
        // up to version 1.6 will cause a spurious IOException to be thrown
        // if the length of the signature file is a multiple of 1024 bytes.
        // As a workaround, add an extra CRLF in this case.
        if ((cout.size() % 1024) == 0) {
            cout.write('\r');
            cout.write('\n');
        }
    }

三、生成CERT.RSA文件

 <span style="white-space:pre">	</span>je = new JarEntry(CERT_RSA_NAME);
        je.setTime(timestamp);
        outputJar.putNextEntry(je);
        writeSignatureBlock(new CMSProcessableByteArray(signedData),
                                publicKey, privateKey, outputJar);

关键代码在writeSignatureBlock(new CMSProcessableByteArray(signedData)中

/** Sign data and write the digital signature to 'out'. */
    private static void writeSignatureBlock(
        CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
        OutputStream out)
        throws IOException,
               CertificateEncodingException,
               OperatorCreationException,
               CMSException {
        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(1);
        certList.add(publicKey);
        JcaCertStore certs = new JcaCertStore(certList);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA")
            .setProvider(sBouncyCastleProvider)
            .build(privateKey);
        gen.addSignerInfoGenerator(
            new JcaSignerInfoGeneratorBuilder(
                new JcaDigestCalculatorProviderBuilder()
                .setProvider(sBouncyCastleProvider)
                .build())
            .setDirectSignature(true)
            .build(sha1Signer, publicKey));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(data, false);

        ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
        DEROutputStream dos = new DEROutputStream(out);
        dos.writeObject(asn1.readObject());
    }

把之前生成的CERT.SF文件,用私有密钥计算出签名,
然后将签名以及公钥信息写入 CERT.RSA 中保存。

时间: 2024-12-25 21:01:12

Android数字签名解析(二)的相关文章

Android数字签名解析(三)

在刚才开始学习android数字签名的相关知识点的时候,被资料中出现的keystore.x509.密钥对.debug.keystore弄的晕头 转向,经过一段时间的了解,总算明白一些. 一.make_key脚本生成密钥对 android源码中自带的工具make_key(development/tools/目录下)可以用来生成RSA密钥对. ./make_key test '/C=CN/ST=SH/L=SH/O=TEST/OU=TEST/CN=TEST' 运行以上命令将生成密钥对test.pk8和

Android Fragment解析(二)

上篇博客中已经介绍了Fragment产生原因,以及一些基本的用法和各种API. 本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragment创建对话框,如何与ActionBar,MenuItem集成等~~ 1.管理Fragment回退栈 类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fr

Android zxing 解析二维码,生成二维码极简demo

zxing 官方的代码很多,看起来很费劲,此demo只抽取了有用的部分,实现了相机预览解码,解析本地二维码,生成二维码三个功能. 简化后的结构如下: 废话少说直接上代码: BaseDecodeHandler: package com.song.zxing.decode; import android.graphics.Bitmap; import android.os.Bundle; import com.google.zxing.BarcodeFormat; import com.google

Android数字签名解析(一)

?一.数字签名概述 所谓"数字签名"就是通过某种password运算生成一系列符号及代码组成电子password进行签名,来取代书写签名或印章. 数字签名有两种功效:一是能确定消息确实是由发送方签名并发出来的.由于别人假冒不了发送方的签名. 二是数字签名能确 定消息的完整性. 由于数字签名的特点是它代表了文件的特征.文件假设发生改变,数字签名的值也将发生变化. 二.Android系统中数字签名的使用范围 眼下在android系统中,在两个地方使用了数据签名,一是应用程序.二是OTA升级

Android Intent 解析之二

服务端Intent执行过程: Sticky:这个类型的BroadCast表示某些Intent需要被保留,当新的应用起来后,需要关注这个消息,但是呢,又不需要启动这个应用来接收此消息,比如耳机插入等消息. 这个函数的主要作用就是根据这个Intent的特点,构造BroadCastRecord加入到不同的列表,等待被处理: 这样发送就到了下面这个函数中了: 控制到了scheduleBroadcastsLocked这里,它的逻辑很简单: private final void scheduleBroadc

Android Bitmap 全面解析(二)加载多张图片的缓存处理

一般少量图片是很少出现OOM异常的,除非单张图片过~大~ 那么就可以用教程一里面的方法了通常应用场景是listview列表加载多张图片,为了提高效率一般要缓存一部分图片,这样方便再次查看时能快速显示~不用重新下载图片但是手机内存是很有限的~当缓存的图片越来越多,即使单张图片不是很大,不过数量太多时仍然会出现OOM的情况了~本篇则是讨论多张图片的处理问题-----------------------------------------------------------------------图片

Android Service完全解析(二)

转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要内容,包括Service的基本用法.Service和Activity进行通信.Service的销毁方式.Service与Thread的关系.以及如何创建前台Service.以上所提到的这些知识点,基本上涵盖了大部分日常开发工作当中可能使用到的Service技术.不过关于Service其实还有一个更加

Android Volley解析(二)之表单提交篇

上一篇文章中,讲了 Volley 的 get 和 post 请求,并且对 volley 的基本使用和基本分析做了讲解,而这篇 blog 将讲解用 volley 实现表单的提交,在看这篇文章之前,如果对 Volley 基本知识不够了解的朋友,可以移驾前往Android Volley解析(一)之GET.POST请求篇 表单提交的数据格式 要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式. 数据包: Connection: keep-

android XMl 解析神奇xstream 二: 把对象转换成xml

前言:对xstream不理解的请看:android XMl 解析神奇xstream 一: 解析android项目中 asset 文件夹 下的 aa.xml 文件 1.Javabeen 代码 package com.android10; public class Product { private String name ; private String age ; public String getName() { return name; } public void setName(Strin