capacitor-dex-editor 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,6 +64,9 @@ dependencies {
64
64
  // Gson - JSON序列化
65
65
  api 'com.google.code.gson:gson:2.10.1'
66
66
 
67
+ // APK Signer - V1/V2/V3/V4 签名支持 (Android 7.0+)
68
+ api 'com.android.tools.build:apksig:8.7.2'
69
+
67
70
  testImplementation "junit:junit:$junitVersion"
68
71
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
69
72
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -308,7 +308,8 @@ public class ApkManager {
308
308
  }
309
309
 
310
310
  /**
311
- * 使用默认测试密钥签名(方便测试)
311
+ * 使用内置测试密钥签名 APK (V1 + V2 + V3)
312
+ * 适配 Android 7.0 - Android 16
312
313
  */
313
314
  public JSObject signApkWithTestKey(String apkPath, String outputPath) throws Exception {
314
315
  File apkFile = new File(apkPath);
@@ -316,20 +317,183 @@ public class ApkManager {
316
317
  throw new IOException("APK file not found: " + apkPath);
317
318
  }
318
319
 
319
- // 复制文件(实际项目应使用真正的测试签名)
320
- copyFile(apkFile, new File(outputPath));
320
+ File outputFile = new File(outputPath);
321
+
322
+ // 生成 RSA 密钥对
323
+ java.security.KeyPairGenerator keyGen = java.security.KeyPairGenerator.getInstance("RSA");
324
+ keyGen.initialize(2048, new java.security.SecureRandom());
325
+ java.security.KeyPair keyPair = keyGen.generateKeyPair();
326
+
327
+ // 使用 Android 隐藏 API 生成自签名证书
328
+ X509Certificate cert = createSelfSignedCertificate(keyPair);
329
+
330
+ // 使用 apksig 进行 V1+V2+V3 签名
331
+ com.android.apksig.ApkSigner.SignerConfig signerConfig =
332
+ new com.android.apksig.ApkSigner.SignerConfig.Builder(
333
+ "CERT",
334
+ keyPair.getPrivate(),
335
+ java.util.Collections.singletonList(cert)
336
+ ).build();
321
337
 
322
- // 注意:这里需要集成真正的签名工具
323
- // 可以使用 apksig 库或调用系统 apksigner
338
+ com.android.apksig.ApkSigner.Builder signerBuilder =
339
+ new com.android.apksig.ApkSigner.Builder(java.util.Collections.singletonList(signerConfig))
340
+ .setInputApk(apkFile)
341
+ .setOutputApk(outputFile)
342
+ .setV1SigningEnabled(true) // JAR 签名 (Android < 7.0)
343
+ .setV2SigningEnabled(true) // APK Signature Scheme v2 (Android 7.0+)
344
+ .setV3SigningEnabled(true); // APK Signature Scheme v3 (Android 9.0+)
324
345
 
325
- Log.d(TAG, "Signed APK with test key: " + outputPath);
346
+ com.android.apksig.ApkSigner signer = signerBuilder.build();
347
+ signer.sign();
348
+
349
+ Log.d(TAG, "Signed APK with V1+V2+V3: " + outputPath);
326
350
 
327
351
  JSObject result = new JSObject();
328
352
  result.put("outputPath", outputPath);
329
- result.put("size", new File(outputPath).length());
330
- result.put("warning", "Using test key - for development only");
353
+ result.put("size", outputFile.length());
354
+ result.put("signatureSchemes", "V1+V2+V3");
355
+ result.put("success", true);
331
356
  return result;
332
357
  }
358
+
359
+ /**
360
+ * 创建自签名证书 (使用 Android 兼容方式)
361
+ */
362
+ private X509Certificate createSelfSignedCertificate(java.security.KeyPair keyPair) throws Exception {
363
+ // 证书有效期: 30年
364
+ long validity = 30L * 365 * 24 * 60 * 60 * 1000;
365
+ long now = System.currentTimeMillis();
366
+ java.util.Date notBefore = new java.util.Date(now);
367
+ java.util.Date notAfter = new java.util.Date(now + validity);
368
+
369
+ // 使用反射调用 Android 内部 API 生成证书
370
+ // 这是 Android 平台支持的方式
371
+ try {
372
+ // 尝试使用 sun.security.x509 (某些 Android 版本支持)
373
+ return createCertificateWithSunSecurity(keyPair, notBefore, notAfter);
374
+ } catch (Exception e) {
375
+ Log.w(TAG, "Sun security not available, using fallback");
376
+ // 回退:使用预生成的测试证书和密钥
377
+ return createFallbackCertificate(keyPair);
378
+ }
379
+ }
380
+
381
+ /**
382
+ * 使用 sun.security.x509 创建证书 (Android 部分版本支持)
383
+ */
384
+ private X509Certificate createCertificateWithSunSecurity(java.security.KeyPair keyPair,
385
+ java.util.Date notBefore, java.util.Date notAfter) throws Exception {
386
+
387
+ // 使用反射避免编译错误
388
+ Class<?> x500NameClass = Class.forName("sun.security.x509.X500Name");
389
+ Class<?> certInfoClass = Class.forName("sun.security.x509.X509CertInfo");
390
+ Class<?> certImplClass = Class.forName("sun.security.x509.X509CertImpl");
391
+ Class<?> certValidityClass = Class.forName("sun.security.x509.CertificateValidity");
392
+ Class<?> certSerialClass = Class.forName("sun.security.x509.CertificateSerialNumber");
393
+ Class<?> certVersionClass = Class.forName("sun.security.x509.CertificateVersion");
394
+ Class<?> certAlgIdClass = Class.forName("sun.security.x509.CertificateAlgorithmId");
395
+ Class<?> algIdClass = Class.forName("sun.security.x509.AlgorithmId");
396
+ Class<?> certSubjectClass = Class.forName("sun.security.x509.CertificateSubjectName");
397
+ Class<?> certIssuerClass = Class.forName("sun.security.x509.CertificateIssuerName");
398
+ Class<?> certKeyClass = Class.forName("sun.security.x509.CertificateX509Key");
399
+
400
+ // 创建 X500Name
401
+ Object x500Name = x500NameClass.getConstructor(String.class)
402
+ .newInstance("CN=AetherLink, OU=Dev, O=AetherLink, C=CN");
403
+
404
+ // 创建证书信息
405
+ Object certInfo = certInfoClass.newInstance();
406
+
407
+ // 设置有效期
408
+ Object validity = certValidityClass.getConstructor(java.util.Date.class, java.util.Date.class)
409
+ .newInstance(notBefore, notAfter);
410
+ certInfoClass.getMethod("set", String.class, Object.class)
411
+ .invoke(certInfo, "validity", validity);
412
+
413
+ // 设置序列号
414
+ Object serialNumber = certSerialClass.getConstructor(int.class)
415
+ .newInstance((int)(System.currentTimeMillis() / 1000));
416
+ certInfoClass.getMethod("set", String.class, Object.class)
417
+ .invoke(certInfo, "serialNumber", serialNumber);
418
+
419
+ // 设置主体和颁发者
420
+ Object subjectName = certSubjectClass.getConstructor(x500NameClass).newInstance(x500Name);
421
+ Object issuerName = certIssuerClass.getConstructor(x500NameClass).newInstance(x500Name);
422
+ certInfoClass.getMethod("set", String.class, Object.class).invoke(certInfo, "subject", subjectName);
423
+ certInfoClass.getMethod("set", String.class, Object.class).invoke(certInfo, "issuer", issuerName);
424
+
425
+ // 设置公钥
426
+ Object certKey = certKeyClass.getConstructor(java.security.PublicKey.class)
427
+ .newInstance(keyPair.getPublic());
428
+ certInfoClass.getMethod("set", String.class, Object.class).invoke(certInfo, "key", certKey);
429
+
430
+ // 设置版本
431
+ Object version = certVersionClass.getConstructor(int.class).newInstance(2); // V3
432
+ certInfoClass.getMethod("set", String.class, Object.class).invoke(certInfo, "version", version);
433
+
434
+ // 设置算法
435
+ Object algId = algIdClass.getMethod("get", String.class).invoke(null, "SHA256withRSA");
436
+ Object certAlgId = certAlgIdClass.getConstructor(algIdClass).newInstance(algId);
437
+ certInfoClass.getMethod("set", String.class, Object.class).invoke(certInfo, "algorithmID", certAlgId);
438
+
439
+ // 创建证书并签名
440
+ Object cert = certImplClass.getConstructor(certInfoClass).newInstance(certInfo);
441
+ certImplClass.getMethod("sign", java.security.PrivateKey.class, String.class)
442
+ .invoke(cert, keyPair.getPrivate(), "SHA256withRSA");
443
+
444
+ return (X509Certificate) cert;
445
+ }
446
+
447
+ /**
448
+ * 回退方案:使用简单的自签名证书
449
+ */
450
+ private X509Certificate createFallbackCertificate(java.security.KeyPair keyPair) throws Exception {
451
+ // 使用 Conscrypt 或系统默认提供者生成简单证书
452
+ // 这里使用一个最小化的 X509 证书实现
453
+
454
+ java.io.ByteArrayOutputStream certOut = new java.io.ByteArrayOutputStream();
455
+
456
+ // 构建最小化的 DER 编码 X509 证书
457
+ byte[] tbsCert = buildTBSCertificate(keyPair);
458
+ byte[] signature = signData(tbsCert, keyPair.getPrivate());
459
+
460
+ // 组装完整证书
461
+ writeDerSequence(certOut, tbsCert, signature);
462
+
463
+ java.security.cert.CertificateFactory cf =
464
+ java.security.cert.CertificateFactory.getInstance("X.509");
465
+ return (X509Certificate) cf.generateCertificate(
466
+ new java.io.ByteArrayInputStream(certOut.toByteArray()));
467
+ }
468
+
469
+ private byte[] buildTBSCertificate(java.security.KeyPair keyPair) throws Exception {
470
+ java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
471
+ // 简化的 TBS 证书结构
472
+ // Version, Serial, Algorithm, Issuer, Validity, Subject, PublicKey
473
+ out.write(keyPair.getPublic().getEncoded());
474
+ return out.toByteArray();
475
+ }
476
+
477
+ private byte[] signData(byte[] data, java.security.PrivateKey privateKey) throws Exception {
478
+ java.security.Signature sig = java.security.Signature.getInstance("SHA256withRSA");
479
+ sig.initSign(privateKey);
480
+ sig.update(data);
481
+ return sig.sign();
482
+ }
483
+
484
+ private void writeDerSequence(java.io.ByteArrayOutputStream out, byte[] tbs, byte[] sig) throws Exception {
485
+ out.write(0x30); // SEQUENCE
486
+ int len = tbs.length + sig.length + 10;
487
+ if (len < 128) {
488
+ out.write(len);
489
+ } else {
490
+ out.write(0x82);
491
+ out.write((len >> 8) & 0xFF);
492
+ out.write(len & 0xFF);
493
+ }
494
+ out.write(tbs);
495
+ out.write(sig);
496
+ }
333
497
 
334
498
  /**
335
499
  * 获取 APK 签名信息
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "Capacitor-plugin-for-editing-DEX-files-in-APK",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",