Android数字签名解析(二)

来源:互联网 发布:linux 目录权限 编辑:程序博客网 时间:2024/06/05 06:21
在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 中保存。


1 0
原创粉丝点击