java判断APK是否使用了V2签名

2023-04-06by@jspp&@ChatGPT.Model: gpt-3.5
jspp

java判断APK是否使用了V2签名

Avatar
Avatar

可以使用以下代码判断APK是否使用了V2签名:

import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;

import com.android.apksig.ApkSigner;
import com.android.apksig.ApkVerifier;
import com.android.apksig.ApkVerifier.Result;
import com.android.apksig.DefaultApkSignerEngine;

public class ApkVerifierUtils {

  /**
   * 验证APK是否使用了V2签名
   * 
   * @param apkPath APK文件路径
   * @return 是否使用了V2签名
   */
  public static boolean isV2SignatureUsed(String apkPath) {
    try {
      // 构建ApkVerifier对象
      ApkVerifier apkVerifier = new ApkVerifier.Builder().build();

      // 读取APK文件
      byte[] apkBytes = ApkVerifierUtils.readBytesFromFile(apkPath);

      // 验证APK签名
      Result result = apkVerifier.verify(apkBytes);

      // 获取签名信息列表
      List<ApkSigner> signers = result.getSigners();

      // 判断是否使用了V2签名
      for (ApkSigner signer : signers) {
        DefaultApkSignerEngine signerEngine = (DefaultApkSignerEngine) signer.getSignerEngine();
        List<X509Certificate> certificates = signerEngine.getCertificates();
        for (X509Certificate certificate : certificates) {
          if (certificate.getEncoded().length > 0) {
            // 如果有V2签名,则返回true
            return true;
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    // 如果没有V2签名,则返回false
    return false;
  }

  /**
   * 从文件中读取字节数组
   * 
   * @param filePath 文件路径
   * @return 字节数组
   * @throws IOException
   */
  public static byte[] readBytesFromFile(String filePath) throws IOException {
    java.nio.file.Path path = java.nio.file.Paths.get(filePath);
    return java.nio.file.Files.readAllBytes(path);
  }
}

使用方法:

String apkPath = "path/to/your/app.apk";
boolean isV2SignatureUsed = ApkVerifierUtils.isV2SignatureUsed(apkPath);
System.out.println("isV2SignatureUsed: " + isV2SignatureUsed);
jspp

java服务端判断APK使用了V1还是V2签名

Avatar
Avatar

可以使用以下代码判断APK是否使用了V1或V2签名:

import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;

import com.android.apksig.ApkSigner;
import com.android.apksig.ApkVerifier;
import com.android.apksig.ApkVerifier.Result;
import com.android.apksig.DefaultApkSignerEngine;

public class ApkVerifierUtils {

  /**
   * 验证APK是否使用了V1签名
   * 
   * @param apkPath APK文件路径
   * @return 是否使用了V1签名
   */
  public static boolean isV1SignatureUsed(String apkPath) {
    try {
      // 构建ApkVerifier对象
      ApkVerifier apkVerifier = new ApkVerifier.Builder().build();

      // 读取APK文件
      byte[] apkBytes = ApkVerifierUtils.readBytesFromFile(apkPath);

      // 验证APK签名
      Result result = apkVerifier.verify(apkBytes);

      // 获取签名信息列表
      List<ApkSigner> signers = result.getSigners();

      // 判断是否使用了V1签名
      for (ApkSigner signer : signers) {
        DefaultApkSignerEngine signerEngine = (DefaultApkSignerEngine) signer.getSignerEngine();
        List<X509Certificate> certificates = signerEngine.getCertificates();
        for (X509Certificate certificate : certificates) {
          if (certificate.getEncoded().length == 0) {
            // 如果没有V1签名,则返回false
            return false;
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    // 如果有V1签名,则返回true
    return true;
  }

  /**
   * 验证APK是否使用了V2签名
   * 
   * @param apkPath APK文件路径
   * @return 是否使用了V2签名
   */
  public static boolean isV2SignatureUsed(String apkPath) {
    try {
      // 构建ApkVerifier对象
      ApkVerifier apkVerifier = new ApkVerifier.Builder().build();

      // 读取APK文件
      byte[] apkBytes = ApkVerifierUtils.readBytesFromFile(apkPath);

      // 验证APK签名
      Result result = apkVerifier.verify(apkBytes);

      // 获取签名信息列表
      List<ApkSigner> signers = result.getSigners();

      // 判断是否使用了V2签名
      for (ApkSigner signer : signers) {
        DefaultApkSignerEngine signerEngine = (DefaultApkSignerEngine) signer.getSignerEngine();
        List<X509Certificate> certificates = signerEngine.getCertificates();
        for (X509Certificate certificate : certificates) {
          if (certificate.getEncoded().length > 0) {
            // 如果有V2签名,则返回true
            return true;
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    // 如果没有V2签名,则返回false
    return false;
  }

  /**
   * 从文件中读取字节数组
   * 
   * @param filePath 文件路径
   * @return 字节数组
   * @throws IOException
   */
  public static byte[] readBytesFromFile(String filePath) throws IOException {
    java.nio.file.Path path = java.nio.file.Paths.get(filePath);
    return java.nio.file.Files.readAllBytes(path);
  }
}

使用方法:

String apkPath = "path/to/your/app.apk";
boolean isV1SignatureUsed = ApkVerifierUtils.isV1SignatureUsed(apkPath);
System.out.println("isV1SignatureUsed: " + isV1SignatureUsed);

boolean isV2SignatureUsed = ApkVerifierUtils.isV2SignatureUsed(apkPath);
System.out.println("isV2SignatureUsed: " + isV2SignatureUsed);

如果isV1SignatureUsedtrue,说明APK使用了V1签名;如果isV2SignatureUsedtrue,说明APK使用了V2签名;如果都为false,说明APK没有签名或签名格式不支持。

jspp

com.android.apksig.ApkSigner 对应的maven坐标地址是什么

Avatar
Avatar

com.android.apksig.ApkSigner类是Android SDK中的一部分,因此没有对应的 Maven 坐标。如果您需要使用该类,可以在您的项目中引入 Android SDK,并确保使用了包含该类的 SDK 版本。如果您使用的是 Gradle,可以在您的build.gradle文件中添加以下依赖:

android {
    // ...
    buildToolsVersion "30.0.3"
}

dependencies {
    // ...
    implementation 'com.android.tools.build:apksig:4.1.0'
}

注意,com.android.apksig.ApkSigner类所在的apksig库是包含在com.android.tools.build:apksig依赖中的。因此,您需要同时引入com.android.tools.build:apksig和其它必要的 Android 库依赖。

jspp

不使用Android SDK 的情况下,如何单独使用com.android.apksig.ApkSigner

Avatar
Avatar

如果您不想使用 Android SDK,可以使用独立的 apksig 库。该库已经发布到 Maven 中央仓库,您可以通过以下 Maven 坐标来获取:

<dependency>
    <groupId>com.android.tools.build</groupId>
    <artifactId>apksig</artifactId>
    <version>4.1.0</version>
</dependency>

或者通过以下 Gradle 依赖:

dependencies {
    implementation 'com.android.tools.build:apksig:4.1.0'
}

在使用该库时,您需要自行处理 APK 签名的细节。以下是一个示例代码,用于验证 APK 是否使用了 V1 签名:

import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;

import com.android.apksig.ApkSigner;
import com.android.apksig.ApkVerifier;
import com.android.apksig.ApkVerifier.Result;
import com.android.apksig.DefaultApkSignerEngine;
import com.android.apksig.apk.ApkFormatException;

public class ApkVerifierUtils {

  /**
   * 验证APK是否使用了V1签名
   * 
   * @param apkPath APK文件路径
   * @return 是否使用了V1签名
   */
  public static boolean isV1SignatureUsed(String apkPath) {
    try {
      // 构建ApkVerifier对象
      ApkVerifier apkVerifier = new ApkVerifier.Builder().build();

      // 读取APK文件
      byte[] apkBytes = ApkVerifierUtils.readBytesFromFile(apkPath);

      // 验证APK签名
      Result result = apkVerifier.verify(apkBytes);

      // 获取签名信息列表
      List<ApkSigner> signers = result.getSigners();

      // 判断是否使用了V1签名
      for (ApkSigner signer : signers) {
        DefaultApkSignerEngine signerEngine = (DefaultApkSignerEngine) signer.getSignerEngine();
        List<X509Certificate> certificates = signerEngine.getCertificates();
        for (X509Certificate certificate : certificates) {
          if (certificate.getEncoded().length == 0) {
            // 如果没有V1签名,则返回false
            return false;
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    // 如果有V1签名,则返回true
    return true;
  }

  /**
   * 从文件中读取字节数组
   * 
   * @param filePath 文件路径
   * @return 字节数组
   * @throws IOException
   */
  public static byte[] readBytesFromFile(String filePath) throws IOException {
    java.nio.file.Path path = java.nio.file.Paths.get(filePath);
    return java.nio.file.Files.readAllBytes(path);
  }
}

使用方法:

String apkPath = "path/to/your/app.apk";
boolean isV1SignatureUsed = ApkVerifierUtils.isV1SignatureUsed(apkPath);
System.out.println("isV1SignatureUsed: " + isV1SignatureUsed);

如果isV1SignatureUsedtrue,说明 APK 使用了 V1 签名。

jspp

C++编写Unicode编码 解码的工具类

Avatar
Avatar

以下是一个 C++ 工具类,用于实现 UTF-8 到 Unicode 编码的转换。该类实现了将 UTF-8 字符串转换为 Unicode 字符串和将 Unicode 字符串转换为 UTF-8 字符串的方法。

#include <string>
#include <vector>
#include <stdexcept>

class UnicodeUtils {
public:
  /**
   * 将 UTF-8 字符串转换为 Unicode 字符串
   *
   * @param utf8str UTF-8 字符串
   * @return Unicode 字符串
   */
  static std::wstring utf8ToUnicode(const std::string& utf8str) {
    std::wstring result;
    result.reserve(utf8str.size());

    for (std::size_t i = 0; i < utf8str.size();) {
      int ch = utf8str[i] & 0xFF;

      if ((ch & 0x80) == 0) {
        // 单字节字符
        result += static_cast<wchar_t>(ch);
        i += 1;
      } else if ((ch & 0xE0) == 0xC0) {
        // 双字节字符
        if (i + 1 >= utf8str.size()) {
          throw std::invalid_argument("Invalid UTF-8 string: truncated character");
        }

        int ch1 = utf8str[i + 1] & 0xFF;
        if ((ch1 & 0xC0) != 0x80) {
          throw std::invalid_argument("Invalid UTF-8 string: malformed character");
        }

        result += static_cast<wchar_t>(((ch & 0x1F) << 6) | (ch1 & 0x3F));
        i += 2;
      } else if ((ch & 0xF0) == 0xE0) {
        // 三字节字符
        if (i + 2 >= utf8str.size()) {
          throw std::invalid_argument("Invalid UTF-8 string: truncated character");
        }

        int ch1 = utf8str[i + 1] & 0xFF;
        int ch2 = utf8str[i + 2] & 0xFF;
        if ((ch1 & 0xC0) != 0x80 || (ch2 & 0xC0) != 0x80) {
          throw std::invalid_argument("Invalid UTF-8 string: malformed character");
        }

        result += static_cast<wchar_t>(((ch & 0x0F) << 12) | ((ch1 & 0x3F) << 6) | (ch2 & 0x3F));
        i += 3;
      } else {
        // 四字节字符
        if (i + 3 >= utf8str.size()) {
          throw std::invalid_argument("Invalid UTF-8 string: truncated character");
        }

        int ch1 = utf8str[i + 1] & 0xFF;
        int ch2 = utf8str[i + 2] & 0xFF;
        int ch3 = utf8str[i + 3] & 0xFF;
        if ((ch1 & 0xC0) != 0x80 || (ch2 & 0xC0) != 0x80 || (ch3 & 0xC0) != 0x80) {
          throw std::invalid_argument("Invalid UTF-8 string: malformed character");
        }

        int codepoint = ((ch & 0x07) << 18) | ((ch1 & 0x3F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F);
        if (codepoint < 0x10000 || codepoint > 0x10FFFF) {
          throw std::invalid_argument("Invalid UTF-8 string: code point out of range");
        }

        result += static_cast<wchar_t>(0xD7C0 + (codepoint >> 10));
        result += static_cast<wchar_t>(0xDC00 + (codepoint & 0x03FF));
        i += 4;
      }
    }

    return result;
  }

  /**
   * 将 Unicode 字符串转换为 UTF-8 字符串
   *
   * @param unicodeStr Unicode 字符串
   * @return UTF-8 字符串
   */
  static std::string unicodeToUtf8(const std::wstring& unicodeStr) {
    std::string result;
    result.reserve(unicodeStr.size() * 3);