zoukankan      html  css  js  c++  java
  • 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() + "
    ");
                for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
                    print.print(att.getKey() + ": " + att.getValue() + "
    ");
                }
                print.print("
    ");
                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('
    ');
                cout.write('
    ');
            }
        }


    三、生成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 中保存。



  • 相关阅读:
    KMP 算法 C++
    java RTTI笔记 之Class学习笔记(摘自java编程思想)
    java sql
    event
    mysql 编写存储过程
    《淘宝技术这十年》重读笔记
    关于“产品”的笔记
    程序员,当你遇到一个“坑”
    C盘清理大作战
    android开发笔记
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5124168.html
Copyright © 2011-2022 走看看