提取Android进程中的RSAPrivateCrtKey和证书

极客 作者:有料 2013-10-31 07:55:31 阅读:156
作者:Gursev Singh Kalra 翻译:Bincker  http://t.qq.com/bincker   最近我评估了一个具有广泛加密控制,以保护客户端服务器通信,并确保其本地存储的Android应用程序它的源代码是完全混淆了,这两个因素结合逆向起来觉得非常困难。这篇博客将详细介绍如何使用Eclipse内存分析工具MAT和Java代码把android应用程序内存当中重构(重建)一个RSA私钥RSAPrivateCrtKey)和dump出 X.509证书的部分细节。 1 使用Eclipse MAT来分析Android系统内存镜像   Eclipse MAT主要主要功能有一个Java堆分析仪,除此之外还具有识别内存泄漏方面检测时候挺广泛使用的。比如可用于识别和Android应用程序的内存转储中的敏感信息,执行一些内存取证等功能…如果你是初次尝试Android的内存分析,我建议你用这个工具,他给你带来更多的便利。你还可以使用下面的链接进行更详细的了解: http://help.eclipse.org/kepler/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html  http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html http://eclipsesource.com/blogs/2013/01/21/10-tips-for-using-the-eclipse-memory-analyzer/ 现在我们回到正题,– 目标android程序   定位加密文件(资料)   作为逆向工程过程中的一部分,我用dex2jar反编译java文件的应用程序apk文件,并开始对其进行分析。虽然应用程序逻辑和检讨其混淆代码,我偶然发现了一个java文件(com.pack.age.name.hbjava)包含实例变量SSLSocketFactory的X509TrustManager型的。显然,这个类执行重要的客户端 – 服务器通信的加密操作。 所以我顺藤摸瓜这class文件,并以识别他的源码,如加密文件的来源和所有企图带领我去的一个一个跳转地址。然后,我觉得使用Eclipse MAT直接查看应用程序堆栈。我启动应用程序,并进行一些操作,以确保应用程序加载所需的加密材料,然后执行下面的HPROF文件的步骤来创建包含应用程序堆转储。  
    从列表中选择应用程序正在运行的应用程序
    选择“Show heap updates”选项为目标应用
    选择“Dump the HPROF File”进行分析。
    因为我有MAT插件安装,ADT Android的内存转储转换为HPROF格式并提交分析。如果你没安装MAT插件,你需要将生成的dump转换程序来自ADT集成的MAT插件可读格式 HPROF-CONV。
dump完了堆栈后,点击“Dominator Tree”可以查看对象结构,找出通过正则过滤掉不需要的东西后具有SSLSocketFactory和X509TrustManager实例变量的类的名称 2 Dump出 证书 X.509证书由不同长度的字节数组组成,且提取的证书速度也很快。 我对字节数组右击 -> navigated to Copy -> Save Value to File -> 选定的位置保存该文件并单击“完成”即可。 MAT 允许你写的char[],字符串,StringBuffer和StringBuilder的复制在一个文本文件中,但它巧妙地当前环境中处理了byte [] 注意一点是 在Windows系统上,导出的文件扩展名是DER扩展名。下面的截图显示提取的证书的步骤   首先选择 “Save Value to File” 功能为 byte[]: 3 下一页保存文件证书为1.der 4 现在你可以看到从Android应用程序提取的CA证书 5 提取RSAPrivateCrtKey   下面我们将看到,第二个重要的组成部分是的RSAPrivateCrtKey和提取,这是确实有点复杂。接下来咱们分步骤进行检索rsaprivatekeycrtkey:
    定位组件构成的rsaprivatecrtkeyspec
    复制所有组件,并将它们存储在文件系统
    从这些组件计算正BigInteger值
    从它的组件构造RSAPrivatecrtKeySpec
    使用RSAPrivatecrtKeySpec 对象构建RSAPrivatecrtKey
    按照 PKCS8 格式把RSAPrivatecrtKey 写入近系统
 可选:
        使用OpenSSL把PKCS8转换为PEM
        从OpenSSL的PEM文件中提取公钥
现在让我们看看在所涉及的细节。   从上面的第一张图像对应的第三个组成部分的一个实例RSAPrivatecrtKeySpec为出发点来构建key   选择com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.bcrsaprivatecrtkey进入MAT的配置树,MAT的支配树填充“属性”选项卡上的关的 需要建立RSAPrivateCrtKeySpec时的几个参与BigInteger的实例信息(类型,实例名称和对象引用)。   以下是参与BigInteger组件构成的rsaprivatecrtkeyspec:  
    modulus
    publicExponent
    privateExponent
    primeP
    primeQ
    primeExponentP
    primeExponentQ
    crtCoefficient
  我用这个信息的BigInteger组件值分隔到不同的变量,因为它们的值复制到文件系统(见下图)。例如,crtCoefficient@0x410b0080在“属性”选项卡(左)被映射到32的整数(右)的数组。模量@0x410afde0是64 INT长表示,2048位密钥大小。由于MAT不知道如何导出的BigInteger对象,我用实际的int[]内参照相应的BigInteger下拉复制出来的二进制内容。   也就是说,我右击同时导出其内容的int[]下拉菜单下的BigInteger。这个过程重复的BigInteger组件8的本地文件和文件被命名为每个属性名称。   这里的属性窗格和相应BigInteger对象在堆 6 Dump对应int[]内容 7 解压后的BigInteger组件下一步是检查,检查是否我能够使用它们重新构造RSAPrivateCrtKeySpec。所以,我决定在前进之前执行两个基本的测试。
    从文件读取单个int值[]被倾倒和匹配他们对值在MAT
    检查所有的BigInteger组件是正数
我写了一些Java代码帮我测试的所有二进制转储针对这两个条件。结果表明,第一个条件是真实所有BigInteger的组件,但不符合第二个条件38的BigInteger的组件有负值,如下图所示。   这里的匹配整数二进制转储MAT(条件1 8 下面是负的值(条件1): 我找遍四周确定为负值的原因和OpenJDK的代码中的注释表明负值可以导致不正确的ASN。 1编码。所以,我包括计算并返回相应的代码 2补负BigInteger值的提供值之前到RSAPrivateCrtKeySpec构造函数。   下面提供了最终的Java代码读取二进制的BigInteger([])组件从文件系统中,并创建RSAPrivateCrtKey在PKCS8格式。
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.security.KeyFactory;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.ArrayList;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class Generate Key {

 public static BigInteger bitIntFromByteArray(int[] byteArrayParam) {
     byte[] localByteArray = new byte[byteArrayParam.length * 4];
     ByteBuffer byteBuffer = ByteBuffer.wrap(localByteArray);
     IntBuffer intBuffer = byteBuffer.asIntBuffer();
     intBuffer.put(byteArrayParam);

     BigInteger bigInteger = new BigInteger(localByteArray);
     if(bigInteger.compareTo(BigInteger.ZERO) < 0)
      bigInteger = new BigInteger(1, bigInteger.toByteArray());
     return bigInteger;
 }

 public static BigInteger bigIntegerFromBinaryFile(String filename) throws IOException {
  ArrayList<Integer> intArrayList = new ArrayList<Integer>();
  DataInputStream inputStream = new DataInputStream(new FileInputStream(filename));
  try {
      while (true) 
          intArrayList.add(inputStream.readInt());
  } catch (EOFException ex) {

  } finally {
   inputStream.close();
  }

  int[] intArray = new int[intArrayList.size()];
  for(int i = 0; i < intArrayList.size(); i++) 
   intArray[i] = intArrayList.get(i);
  return bitIntFromByteArray(intArray);

 }

 public static void main(String[] args) throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, FileNotFoundException, IOException, ClassNotFoundException {
  Security.addProvider(new BouncyCastleProvider());

  BigInteger crtCoefficient = bigIntegerFromBinaryFile("h:\\key-coeffs\\crtCoefficient");
  BigInteger modulus = bigIntegerFromBinaryFile("h:\\key-coeffs\\modulus");
  BigInteger primeExponentP = bigIntegerFromBinaryFile("h:\\key-coeffs\\primeExponentP");
  BigInteger primeExponentQ = bigIntegerFromBinaryFile("h:\\key-coeffs\\primeExponentQ");
  BigInteger primeP = bigIntegerFromBinaryFile("h:\\key-coeffs\\primeP");  
  BigInteger primeQ = bigIntegerFromBinaryFile("h:\\key-coeffs\\primeQ");
  BigInteger privateExponent = bigIntegerFromBinaryFile("h:\\key-coeffs\\privateExponent");
  BigInteger publicExponent = bigIntegerFromBinaryFile("h:\\key-coeffs\\publicExponent");

  System.out.println("crtCoefficient\t" + crtCoefficient);
  System.out.println("modulus\t" + modulus);
  System.out.println("primeExponentP\t" + primeExponentP);
  System.out.println("primeExponentQ\t" + primeExponentQ);
  System.out.println("primeP\t" + primeP);
  System.out.println("primeQ\t" + primeQ);
  System.out.println("privateExponent\t" + privateExponent);
  System.out.println("publicExponent\t" + publicExponent);

  RSAPrivateCrtKeySpec spec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);
  KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
  PrivateKey privateKey = factory.generatePrivate(spec);
  System.out.println(privateKey);
  PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
  FileOutputStream fos = new FileOutputStream( "h:\\key-coeffs\\private-pkcs8.der");
  fos.write(pkcs8EncodedKeySpec.getEncoded());
  fos.close();
 } 
}
  PEM转换PKCS8 下一步过程是要转换的私有密钥的PEM从PKCS8格式文件,然后通过OpenSSL命令生成的私钥与公钥:  
openssl pkcs8 –inform DER –nocrypt –in private-pkcs8.der –out privatePem.pem

openssl rsa –in privatePem.pem –pubout
  这里OpenSSL的转换PKCS8 9 这里OpenSSL的转换PKCS8 10 我们可以使用OpenSSL从privatePem.pem文件提取公钥 11 结论 内存分析是一种强大的技术,可以用来识别和提取运行应用程序的敏感信息然后,可以使用所提取的信息来搞定客户端的安全控制机制   原文:http://blog.opensecurityresearch.com/2013/10/extracting-rsaprivatecrtkey-and.html 作者:Gursev Singh Kalra

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

选择AiDeep,让人工智能为你工作:http://www.aideep.com/
四季很好,只要有你,文娱排行榜:http://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

关注网络尖刀微信公众号
随时掌握互联网精彩