- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章给Android的APK程序签名和重新签名的方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
签名工具的使用 Android源码编译出来的signapk.jar既可给apk签名,也可给rom签名的。使用格式:
1
|
java –jar signapk.jar [-w] publickey.x509[.pem] privatekey.pk8 input.jar output.jar
|
signapk.java 。
1) main函数 main函数会生成公钥对象和私钥对象,并调用addDigestsToManifest函数生成清单对象Manifest后,再调用signFile签名.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public
static
void
main(String[] args) {
//...
boolean
signWholeFile =
false
;
int
argstart =
0
;
/*如果对ROM签名需传递-w参数*/
if (args[0].equals("-w")) {
signWholeFile = true;
argstart = 1;
}
// ...
try {
File publicKeyFile = new File(args[argstart+0]);
X509Certificate publicKey = readPublicKey(publicKeyFile);
PrivateKey privateKey = readPrivateKey(new File(args[argstart+1]));
inputJar = new JarFile(new File(args[argstart+2]), false);
outputFile = new FileOutputStream(args[argstart+3]);
/*对ROM签名,读者可自行分析,和Apk饿签名类似,但是它会添加otacert文件*/
if (signWholeFile) {
SignApk.signWholeFile(inputJar, publicKeyFile, publicKey,
privateKey, outputFile);
}
else {
JarOutputStream outputJar = new JarOutputStream(outputFile);
outputJar.setLevel(9);
/*addDigestsToManifest会生成Manifest对象,然后调用signFile进行签名*/
signFile(addDigestsToManifest(inputJar), inputJar,
publicKeyFile, publicKey, privateKey, outputJar);
outputJar.close();
}
}
catch
(Exception e) {
e.printStackTrace();
System.exit(
1
);
}
finally
{
//...
}
}
|
2) addDigestsToManifest 首先我们得理解Manifest文件的结构,Manifest文件里用空行分割成多个段,每个段由多个属性组成,第一个段的属性集合称为主属性集合,其它段称为普通属性集合,普通属性集合一般会有Name属性,作为该属性集合所在段的名字。Android的manifeset文件会为zip的所有文件各自建立一个段,这个段的Name属性的值就是该文件的path+文件名,另外还有一个SHA1-Digest的属性,该属性的值是对文件的sha1摘要用base64编码得到的字符串。 Manifest示例:
1
2
3
4
5
6
7
8
9
10
11
|
Manifest-Version: 1.0
Created-By: 1.6.0-rc (Sun Microsystems Inc.)
Name: res/drawable-hdpi/user_logout.png
SHA1-Digest: zkQSZbt3Tqc9myEVuxc1dzMDPCs=
Name: res/drawable-hdpi/contacts_cancel_btn_pressed.png
SHA1-Digest: mSVZvKpvKpmgUJ9oXDJaTWzhdic=
Name: res/drawable/main_head_backgroud.png
SHA1-Digest: fe1yzADfDGZvr0cyIdNpGf/ySio=
|
Manifest-Version属性和Created-By所在的段就是主属性集合,其它属性集合就是普通属性集合,这些普通属性集合都有Name属性,作为该段的名字。 addDigestsToManifest源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
private
static
Manifest addDigestsToManifest(JarFile jar)
throws
IOException, GeneralSecurityException {
Manifest input = jar.getManifest();
Manifest output =
new
Manifest();
Attributes main = output.getMainAttributes();
if
(input !=
null
) {
main.putAll(input.getMainAttributes());
}
else
{
main.putValue(
"Manifest-Version"
,
"1.0"
);
main.putValue(
"Created-By"
,
"1.0 (Android SignApk)"
);
}
MessageDigest md = MessageDigest.getInstance(
"SHA1"
);
byte
[] buffer =
new
byte
[
4096
];
int
num;
// We sort the input entries by name, and add them to the
// output manifest in sorted order. We expect that the output
// map will be deterministic.
TreeMap<String, JarEntry> byName =
new
TreeMap<String, JarEntry>();
for
(Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
JarEntry entry = e.nextElement();
byName.put(entry.getName(), entry);
}
for
(JarEntry entry: byName.values()) {
String name = entry.getName();
if
(!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
!name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
!name.equals(OTACERT_NAME) &&
(stripPattern ==
null
||
!stripPattern.matcher(name).matches())) {
InputStream data = jar.getInputStream(entry);
/*计算sha1*/
while ((num = data.read(buffer)) > 0) {
md.update(buffer, 0, num);
}
Attributes attr = null;
if (input != null) attr = input.getAttributes(name);
attr = attr != null ? new Attributes(attr) : new Attributes();
/*base64编码sha1值得到SHA1-Digest属性的值*/
attr.putValue(
"SHA1-Digest"
,
new
String(Base64.encode(md.digest()),
"ASCII"
));
output.getEntries().put(name, attr);
}
}
return
output;
}
|
3) signFile 先将inputjar的所有文件拷贝至outputjar,然后生成Manifest.MF,CERT.SF和CERT.RSA 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
static
void
signFile(Manifest manifest, JarFile inputJar,
File publicKeyFile, X509Certificate publicKey, PrivateKey privateKey,
JarOutputStream outputJar)
throws
Exception {
// Assume the certificate is valid for at least an hour.
long
timestamp = publicKey.getNotBefore().getTime() + 3600L *
1000
;
JarEntry je;
// 拷贝文件
copyFiles(manifest, inputJar, outputJar, timestamp);
// 生成MANIFEST.MF
je =
new
JarEntry(JarFile.MANIFEST_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
manifest.write(outputJar);
// 调用writeSignatureFile 生成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);
// 非常关键的一步 生成 CERT.RSA
je =
new
JarEntry(CERT_RSA_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureBlock(
new
CMSProcessableByteArray(signedData),
publicKey, privateKey, outputJar);
}
|
4) writeSignatureFile 生成CERT.SF,其实是对MANIFEST.MF的各个段再次计算Sha1摘要得到CERT.SF.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
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"
);
// 添加Manifest.mf的sha1摘要
manifest.write(print);
print.flush();
main.putValue(
"SHA1-Digest-Manifest"
,
new
String(Base64.encode(md.digest()),
"ASCII"
));
//对MANIFEST.MF的各个段计算sha1摘要
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'
);
}
}
|
5) writeSignatureBlock 采用SHA1withRSA算法对CERT.SF计算摘要并加密得到数字签名,使用的私钥是privateKey,然后将数字签名和公钥一起存入CERT.RSA。这里使用了开源库bouncycastle来签名.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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();
//签名算法是SHA1withRSA
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());
}
|
采用命令行重新签名APK 重新签名apk,其实也有最简单的方法,即下载一个重新签名的工具re-sign.jar,将apk拖进此工具的窗口就生成了重新签名的apk了。下面我就来讲讲复杂的重新签名的方式:采用命令行方法。 1、配置环境,需安装jdk,sdk 2、在已成功安装jdk的目录中找到jarsigner.exe文件,本机的目录如下:C:\Program Files\Java\jdk1.8.0_20\bin 3、去除准备重新签名的apk本身的签名(fantongyo.apk) 将apk以Winrar方式打开,删除META-INF文件夹即可,并将此Apk文件拷贝至C:\Program Files\Java\jdk1.8.0_20\bin目录中 Apk压缩包内容解析: 1.META-INF目录:存放签名后的CERT和MANIFEST文件,用于识别软件的签名及版本信息 2.rest目录:存放各种Android原始资源,包括:动画anim、图片drawable、布局layout、菜单、xml等等 3.AndroidManifest.xml编码后的Android项目描述文件,包括了Android项目的名称、版限、程序组件描述等等 4.Classes.dex编译后Class被dx程序转换成Dalvik虚拟机的可执行字节码文件 5.Resources.arsc所有文本资源的编译产物,里面包含了各Location对应的字符串资源 4、重新签名Apk文件 方法一:通过命令重新生成AndroidApk包签名证书后再重新签名Apk文件 1.在cmd中切换到jdk的bin目录中:cd C:\Program Files\Java\jdk1.8.0_20\bin 回车 2.再输入以下的命令:
1
2
3
4
5
6
7
8
|
Keytool -genkey -
alias
fantongyo.keystore -keyalg RSA -validity 20000 -keystore fantongyo.keystore
/*解释:keytool工具是Java JDK自带的证书工具
-genkey参数表示:要生成一个证书(版权、身份识别的安全证书)
-
alias
参数表示:证书有别名,-
alias
fantongyo.keystore表示证书别名为:fantongyo
-keyalg RSA表示加密类型,RSA表示需要加密,以防止别人盗取
-validity 20000表示有效时间20000天( K3
-keystore fantongyo.keystore表示要生成的证书名称为fantongyo
*/
|
输入完回车后屏幕显示: 输入keystore密码:[密码不回显](一般建议使用20位,最好记下来后面还要用) 再次输入新密码:[密码不回显]( o' ^$ _( F( K& I0 您的名字与姓氏是什么? [Unknown]:fantongyo 您的组织单位名称是什么? [Unknown]:fantong 您的组织名称是什么? [Unknown]:life 您所在的城市或区域名称是什么?) L# V' |. E0 f; { [Unknown]:shenzhen 您所在的州或省份名称是什么? [Unknown]:guangdong 该单位的两字母国家代码是什么 [Unknown]:CN CN=fantongyo, U=fantong, O=fantong team, L=shenzhen, ST=guangdong, C=CN正确吗? [否]:Y 输入< mine.keystore>的主密码 (如果和keystore密码相同,按回车): 查看C:\Program Files\Java\jdk1.8.0_20\bin目录下,生成了一个签名用的证书文件 fantongyo.keystore 3.重新签名Apk文件 在cmd中输入:jarsigner –verbose –keystore fantongyo.keystore –signedjar fantongyo_signed.apk fantongyo.apk fantongyo.keystore /*解释:* ^, {& k1 Z. M* P/ M+ K5 n5 hjarsigner是Java的签名工具# K8 ~% s# Y. @6 P -verbose参数表示:显示出签名详细信息 -keystore表示使用当前目录中的fantongyo.keystore签名证书文件。 -signedjar fantongyo_signed.apk表示签名后生成的APK名称,% v! a7 e2 v4 W# ]; Gfantongyo.apk表示未签名 的APK Android软件,fantongyo.keystore表示别名 */ 输入完回车后屏幕显示: jar已签名.
在C:\Program Files\Java\jdk1.8.0_20\bin目录下已重新生成fantongyo_signed.apk文件 。
方法2、以android自带的debug.keystore重新签名Apk文件 1.打开eclipse,菜单栏Window—>Preferences—>Android—>Build—>Default debug keystore目录(我的编辑器显示:C:\Users\Administrator\.android\debug.keystore) 2.将debug.keystore文件拷贝至C:\Program Files\Java\jdk1.8.0_20\bin目录下 3.在cmd中切换到jdk的bin目录中:cd C:\Program Files\Java\jdk1.8.0_20\bin 回车 4.再输入以下的命令:
。
1
|
cd
E:\SoftWare\adt-bundle-windows-x86-20140702\sdk\build-tools\android-4.4W
|
6.再输入:zipalign 4 fantongyo.apk fantongyo_signed.apk即可(fantongyo_signed.apk是 重新签名后的apk文件) 。
最后此篇关于给Android的APK程序签名和重新签名的方法的文章就讲到这里了,如果你想了解更多关于给Android的APK程序签名和重新签名的方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我得到了这个printHashKey函数,它运行良好。 fun printHashKey() { try { val info : PackageInfo = packageM
如何使用正确的签名 key 为我的 Android 应用包签名? 最佳答案 我尝试在此处和 this question 中使用多个答案, 但不知何故我收到了这个错误,因为我的 android/app/
我的 gradle 文件中有这个: android { signingConfigs { mySigningConfig { keyAlias 'the
请至少选择一个签名版本以在 Android Studio 2.3 中使用 现在在 Android Studio 中生成一个签名的 APK 时,它显示了两个选项(复选框),即 1. V1(Jar 签名)
我想表示一些标量值(例如整数或字符串)通过它的实际值或一些 NA 值,然后存储它们在集合中(例如列表)。目的是处理缺失值。 为此,我实现了一个签名 module type Scalar = sig
为什么这不完全有效? sum :: (Num a, Num b) => a -> b -> c sum a b = a + b 当然,错误消息与签名有关,但我仍然不明白原因。 Couldn't mat
谢谢帮助,我的问题是关于从下面的代码中收到的 ax 值? mov al,22h mov cl,0fdh imul cl 真机结果:ff9a 我的预期:00:9a(通过二进制相乘) 第一个数字是 22h
我有一个注释: import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.
我从对物体的思考中得出了一个术语。当我们扩展一个类时,扩展类将具有与父类相同的签名,因此术语 IS-A 来自...示例 class Foo{ } class Foo2 extends Foo{ } c
我需要在有符号整数和它们作为字节序列的内部表示之间进行转换。在 C 中,我使用的函数如下: unsigned char hibyte(unsigned short i) {return i>>8;}
我正在尝试使用给定的 RSA 参数对一些数据进行签名。 我给出了模数、指数、D、DP、DQ、P、Q 和 InverseQ。什么库或方法最容易使用来计算此签名。在 C# 中,一旦您提供参数,它们就会有一
这些签名之间有什么区别? T * f(T & identifier); T & f(T & identifier); T f(T & identifier); void f(T * identifie
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Where and why do I have to put the “template” and “typ
我有一个签名,我需要在签名旁边添加图片。但我不确定 css 的确切程度和内容。目前它显示在文字下方,我应该把图片放在哪里?在相同的 tr 或 td 中?
查看 LinkedHashMap 的 JDK 源代码,我注意到这个类被声明为: public class LinkedHashMap extends HashMap im
背景:我继承了一个基于 linux 的嵌入式系统,其中包含一个 SMTP 代理和一些我不得不忍受的古怪限制。它位于 SMTP 客户端和服务器之间。当 SMTP 客户端连接时,代理会打开与服务器的连接,
这是 C++17 形式的规则 ([basic.lval]/8),但它在其他标准中看起来很相似(在 C++98 中是“lvalue”而不是“glvalue”): 8 If a program attem
我有一个注释: import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.
我即将开展一个项目,希望使用电子签名板使用 C# 捕获客户的签名、在设备上显示文本等。 现在,在我开始做进一步的研究之前,我想向你们征求一些意见/建议,我应该使用哪些设备.. 我现在的要求非常笼统:我
呢喃自己在心中开始扩张地盘,仿佛制式地广播了三次。 漾起的涟绮,用谈不上精腻的手段。 拒绝天亮,却又贪恋着贪恋多情的日光。 川流不息的画面是他们,而我的落幕停在右脚,它渴望着下台,而我只剩自言
我是一名优秀的程序员,十分优秀!