本文目录导读:

我来为您介绍一个Java代码签名(Code Signing)的完整案例,涵盖从生成证书到签名打包的全过程。
准备工作
1 使用keytool生成自签名证书
# 生成密钥库和自签名证书 keytool -genkey -alias mykey -keyalg RSA -keysize 2048 -keystore mykeystore.jks -validity 365 # 参数说明: # -alias: 密钥别名 # -keyalg: 加密算法 # -keysize: 密钥长度 # -keystore: 密钥库文件名 # -validity: 有效期(天)
2 查看生成的证书
# 查看密钥库内容 keytool -list -v -keystore mykeystore.jks # 导出证书(可选) keytool -export -alias mykey -keystore mykeystore.jks -file mycert.crt
Java代码签名示例
1 创建需要签名的Java程序
// MainApplication.java
import javax.swing.*;
import java.awt.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class MainApplication extends JFrame {
public MainApplication() {
setTitle("代码签名示例");
setSize(400, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
JButton btn = new JButton("执行特权操作");
btn.addActionListener(e -> {
// 需要特权的操作
String result = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> {
// 这里执行需要特权的操作
return System.getProperty("user.name");
}
);
JOptionPane.showMessageDialog(this, "当前用户: " + result);
});
panel.add(btn);
add(panel);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new MainApplication().setVisible(true);
});
}
}
2 编译Java程序
# 编译Java文件 javac MainApplication.java # 打JAR包 jar cvf myapp.jar *.class
签名JAR文件
1 使用jarsigner签名
# 基本签名命令 jarsigner -keystore mykeystore.jks -storepass 密码 -keypass 密码 myapp.jar mykey # 使用时间戳服务器(推荐) jarsigner -keystore mykeystore.jks -tsa http://timestamp.digicert.com myapp.jar mykey # 签名后验证 jarsigner -verify myapp.jar
2 完整的签名脚本
#!/bin/bash
# sign_jar.sh
KEYSTORE="mykeystore.jks"
STORE_PASS="your_keystore_password"
KEY_PASS="your_key_password"
ALIAS="mykey"
JAR_FILE="myapp.jar"
TSA_URL="http://timestamp.digicert.com"
echo "开始签名JAR文件..."
# 签名
jarsigner -keystore $KEYSTORE \
-storepass $STORE_PASS \
-keypass $KEY_PASS \
-tsa $TSA_URL \
$JAR_FILE $ALIAS
# 验证签名
echo "验证签名..."
jarsigner -verify -verbose -certs $JAR_FILE
if [ $? -eq 0 ]; then
echo "签名成功且验证通过!"
else
echo "签名验证失败!"
exit 1
fi
权限策略配置
1 创建策略文件
// myapp.policy
grant codeBase "file:/path/to/myapp.jar" {
permission java.util.PropertyPermission "user.name", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.io.FilePermission "/tmp/*", "read,write";
permission java.net.SocketPermission "localhost:1024-", "connect";
};
2 使用策略文件运行
# 使用安全策略运行 java -Djava.security.policy=myapp.policy -jar myapp.jar # 或使用安全管理器 java -Djava.security.manager -Djava.security.policy=myapp.policy -jar myapp.jar
Maven项目示例
1 pom.xml配置
<project>
<build>
<plugins>
<!-- Maven JAR Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.example.MainApplication</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!-- Maven Jarsigner Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>sign</id>
<phase>package</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<keystore>${basedir}/mykeystore.jks</keystore>
<alias>mykey</alias>
<storepass>${keystore.password}</storepass>
<keypass>${key.password}</keypass>
<tsa>http://timestamp.digicert.com</tsa>
</configuration>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
完整示例代码
1 安全检查示例
// SecurityCheckExample.java
import java.security.*;
import javax.swing.*;
public class SecurityCheckExample {
public static void main(String[] args) {
// 检查是否有签名
CodeSource cs = SecurityCheckExample.class.getProtectionDomain().getCodeSource();
if (cs != null) {
java.security.cert.Certificate[] certs = cs.getCertificates();
if (certs != null) {
System.out.println("代码已签名,证书数量: " + certs.length);
for (java.security.cert.Certificate cert : certs) {
System.out.println("证书类型: " + cert.getType());
}
} else {
System.out.println("警告: 代码未签名!");
JOptionPane.showMessageDialog(null,
"未签名的应用程序可能受限",
"安全警告",
JOptionPane.WARNING_MESSAGE);
}
}
}
}
2 权限请求示例
// PermissionRequestExample.java
import java.security.*;
import javax.swing.*;
public class PermissionRequestExample {
public static void requestFilePermission() {
try {
// 请求文件读取权限
Permission perm = new java.io.FilePermission("/etc/passwd", "read");
AccessController.checkPermission(perm);
System.out.println("权限检查通过");
} catch (AccessControlException e) {
System.out.println("权限不足: " + e.getMessage());
JOptionPane.showMessageDialog(null,
"需要额外的权限来执行此操作",
"权限请求",
JOptionPane.INFORMATION_MESSAGE);
}
}
public static void main(String[] args) {
requestFilePermission();
}
}
验证签名信息
1 程序化验证
// VerifySignature.java
import java.util.jar.*;
import java.security.cert.*;
import java.io.*;
public class VerifySignature {
public static boolean verifyJarSignature(String jarPath) {
try (JarFile jar = new JarFile(jarPath, true)) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
// 跳过目录和签名相关文件
if (entry.isDirectory() ||
entry.getName().startsWith("META-INF/")) {
continue;
}
// 验证条目签名
Certificate[] certs = entry.getCertificates();
if (certs == null) {
System.out.println("未签名条目: " + entry.getName());
return false;
}
// 验证证书链
for (Certificate cert : certs) {
if (cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate) cert;
try {
x509Cert.checkValidity();
System.out.println("证书有效: " +
x509Cert.getSubjectDN());
} catch (CertificateExpiredException e) {
System.out.println("证书已过期: " +
x509Cert.getSubjectDN());
return false;
}
}
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static void main(String[] args) {
if (args.length > 0) {
boolean isValid = verifyJarSignature(args[0]);
System.out.println("签名验证结果: " + (isValid ? "通过" : "失败"));
}
}
}
最佳实践建议
- 使用时间戳:避免证书过期后签名失效
- 密码管理:不要在代码中硬编码密码,使用环境变量或配置文件
- 证书备份:妥善保管密钥库文件
- 定期更新:证书过期前更新签名
- 最小权限:只授予必要的最小权限
这个案例展示了Java代码签名从生成证书到签名验证的完整流程,实际生产环境中建议使用权威CA(如DigiCert、GlobalSign)颁发的代码签名证书。