您的位置:首页 > 编程语言 > Java开发

Java生成XML数字签名

2017-12-14 17:31 483 查看

一、生成证书文件

1、安装openssl,装好后输入openssl进入openssl交互界面

[root@kolbe ~]# yum install openssl
[root@kolbe ~]# openssl
OpenSSL>


2、生成RSA私钥文件,并输出到当前目录,命名为 rsa_private_key.pem 2018

OpenSSL> genrsa -out rsa_private_key.pem 2048

Generating RSA private key, 2048 bit long modulus
....................................................................................................................+++
....................................................................................................................................+++
e is 65537 (0x10001)


3、将RSA私钥转换成PKCS8格式,并输出到当前目录,命名为 pkcs8_rsa_private_key.pem

OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -out pkcs8_rsa_private_key.pem -nocrypt

writing RSA key


4、根据私钥文件生成RSA公钥文件,并输出到当前目录,命名为 rsa_public_key.pem

OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem


5、通过私钥文件创建csr证书文件,并输出到当前目录,命名为 rsa_cert.csr

OpenSSL> req -new -out rsa_cert.csr -key rsa_private_key.pem

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----

# 输入国家代码
Country Name (2 letter code) [XX]:cn

# 输入省份
State or Province Name (full name) []:sichuan

# 输入城市
Locality Name (eg, city) [Default City]:chengdu

# 输入公司
Organization Name (eg, company) [Default Company Ltd]:kolbe

# 输入部门
Organizational Unit Name (eg, section) []:kolbe

# 输入姓名
Common Name (eg, your name or your server's hostname) []:kolbe

# 输入邮箱
Email Address []:kolbe@email.com

Please enter the following 'extra' attributes
to be sent with your certificate request

# 输入密码(留空)
A challenge password []:

# 可选的公司(留空)
An optional company name []:


6、自签署证书,并输出到当前目录,命名为 rsa_cert.pem

OpenSSL> x509 -req -in rsa_cert.csr -out rsa_cert.pem -signkey rsa_private_key.pem

Signature ok
subject=/C=cn/ST=sichuan/L=chengdu/O=kolbe/OU=kolbe/CN=kolbe/emailAddress=kolbe@email.com
Getting Private key


至此我们共生成了五个证书文件:

rsa_private_key.pem、pkcs8_rsa_private_key.pem、rsa_public_key.pem、rsa_cert.csr、rsa_vert.pem

二、Java 为 Xml 生成 Signature

1、Key加载工具类

package cn.kolbe.xml.generator;

import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Decoder;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
* 证书加载工具
*
* @author Kolbe
*/
public class KeyUtil {

/**
* 从指定路径获取公钥
*
* @param filePath 公钥文件路径
* @return 公钥
*/
public static PublicKey loadPublicKey(String filePath) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
StringBuilder sb = new StringBuilder();
String s;
while ((s = reader.readLine()) != null) {
if (s.charAt(0) != '-') {
sb.append(s);
sb.append('\r');
}
}
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] buffer = base64Decoder.decodeBuffer(sb.toString());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
IOUtils.closeQuietly(reader);
}
}

/**
* 从指定文件路径获取私钥
*
* @param filePath 私钥文件路径
* @return 私钥
*/
public static PrivateKey loadPrivateKey(String filePath) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
StringBuilder sb = new StringBuilder();
String s;
while ((s = reader.readLine()) != null) {
if (s.charAt(0) != '-') {
sb.append(s);
sb.append('\r');
}
}
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] buffer = base64Decoder.decodeBuffer(sb.toString());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
IOUtils.closeQuietly(reader);
}
}

/**
* 从指定路定,加载签署过的证书文件
*
* @param filePath 证书文件路径
* @return 证书文件
*/
public static Certificate loadCertificate(String filePath) {
InputStream is = null;
try {
is = new FileInputStream(filePath);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(is);
return certificate;
} catch (Exception e) {
throw new RuntimeException("Load certificate error:", e);
} finally {
IOUtils.closeQuietly(is);
}
}

}


2、KeyInfo 选择器

package cn.kolbe.xml.generator;

import javax.xml.crypto.*;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import java.security.Key;
import java.security.PublicKey;

/**
* @author Kolbe
*/
public class X509KeySelector extends KeySelector {

private PublicKey key;

public X509KeySelector(PublicKey key) {
this.key = key;
}

@Override
public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException {
return new KeySelectorResult() {
@Override
public Key getKey() {
return key;
}
};
}
}


3、Xml签名请求参数

package cn.kolbe.xml.generator;

/**
* Xml签名请求参数
*
* @author Kolbe
*/
public class XmlSignatureParam {

/**
* 私钥地址(PKCS8格式)
*/
private String privateKeyPath;

/**
* 公钥地址
*/
private String publicKeyPath;

/**
* 证书文件地址(经过签署过的证书文件)
*/
private String certificatePath;

/**
* 待生成签名的xml文件路径
*/
private String sourceXmlPath;

/**
* 目标生成签名的xml文件路径
*/
private String targetXmlPath;

public String getPrivateKeyPath() {
return privateKeyPath;
}

public XmlSignatureParam setPrivateKeyPath(String privateKeyPath) {
this.privateKeyPath = privateKeyPath;
return this;
}

public String getPublicKeyPath() {
return publicKeyPath;
}

public XmlSignatureParam setPublicKeyPath(String publicKeyPath) {
this.publicKeyPath = publicKeyPath;
return this;
}

public String getSourceXmlPath() {
return sourceXmlPath;
}

public XmlSignatureParam setSourceXmlPath(String sourceXmlPath) {
this.sourceXmlPath = sourceXmlPath;
return this;
}

public String getTargetXmlPath() {
return targetXmlPath;
}

public XmlSignatureParam setTargetXmlPath(String targetXmlPath) {
this.targetXmlPath = targetXmlPath;
return this;
}

public String getCertificatePath() {
return certificatePath;
}

public XmlSignatureParam setCertificatePath(String certificatePath) {
this.certificatePath = certificatePath;
return this;
}
}


4、Xml签名工具类

package cn.kolbe.xml.generator;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.XPathFilter2ParameterSpec;
import javax.xml.crypto.dsig.spec.XPathType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Xml签名工具类
*
* @author Kolbe
*/
public class XmlSignatureUtil {

public static void main(String[] args) throws Exception {
XmlSignatureParam param = new XmlSignatureParam();
param
// 私钥必需使用PKCS8格式
.setPrivateKeyPath("/opt/key/pkcs8_rsa_private_key.pem")
.setPublicKeyPath("/opt/key/rsa_public_key.pem")
// 证书必须是经过签署过的
.setCertificatePath("/opt/key/rsa_cert.pem")
.setSourceXmlPath("/opt/key/source.xml")
.setTargetXmlPath("/opt/key/target.xml");
signature(param);
System.out.println("Xml verify result:" + verify(param));
}

/**
* 对指定xml文件进行签名
*
* @param param 请求参数
* @throws Exception 签名异常
*/
public static void signature(XmlSignatureParam param) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(param.getSourceXmlPath()));
XMLSignatureFactory fac = XMLSignatureFactory.getInstance();
List<Transform> transforms = new ArrayList<>();
// 只校验文档的 Header 和 Body 部分
XPathType xPathType = new XPathType("//*[local-name()='Header' or local-name()='Body']", XPathType.Filter.INTERSECT);
XPathFilter2ParameterSpec xPathFilter = new XPathFilter2ParameterSpec(Collections.singletonList(xPathType));
Transform transform = fac.newTransform(Transform.XPATH2, xPathFilter);
transforms.add(transform);
DigestMethod digestMethod = fac.newDigestMethod(DigestMethod.SHA256, null);
Reference reference = fac.newReference("", digestMethod, transforms, null, null);

CanonicalizationMethod c14nWithCommentMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
SignatureMethod signatureMethod = fac.newSignatureMethod(("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"), null);
SignedInfo signedInfo = fac.newSignedInfo(c14nWithCommentMethod, signatureMethod, Collections.singletonList(reference));

KeyInfoFactory keyInfoFac = fac.getKeyInfoFactory();
KeyInfoFactory factory = KeyInfoFactory.getInstance("DOM");

X509Data x509Data = factory.newX509Data(Collections.singletonList(KeyUtil.loadCertificate(param.getCertificatePath())));
KeyInfo keyInfo = keyInfoFac.newKeyInfo(Collections.singletonList(x509Data));

XMLSignature signature = fac.newXMLSignature(signedInfo, keyInfo);
DOMSignContext dsc = new DOMSignContext(KeyUtil.loadPrivateKey(param.getPrivateKeyPath()), doc.getDocumentElement());
// 添加 Signature 部分的命名空间
dsc.setDefaultNamespacePrefix("ns3");

signature.sign(dsc);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(param.getTargetXmlPath()));
transformer.transform(source, result);
}

/**
* 对指定带签名的xml文件进行校验
*
* @param param 请求参数
* @return true 校验成功, false 校验失败
* @throws Exception 异常
*/
public static boolean verify(XmlSignatureParam param) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(param.getTargetXmlPath());
Node nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0);
DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(KeyUtil.loadPublicKey(param.getPublicKeyPath())), nl);
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = factory.unmarshalXMLSignature(valContext);
return signature.validate(valContext);
}

}


5、待签名的Xml文件示例 source.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Shipment xmlns="http://www.kolbe.cn/Shipment" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#">
<Header>
<Customer>Kolbe</Customer>
</Header>
<Body>
<Device>
<DeviceNo>100000000001</DeviceNo>
<DeviceType>1</DeviceType>
</Device>
</Body>
</Shipment>


6、签名过的Xml文件示例 target.xml

<?xml version="1.0" encoding="utf-8"?>

<Shipment xmlns="http://www.kolbe.cn/Shipment" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#">
<Header>
<Customer>Kolbe</Customer>
</Header>
<Body>
<Device>
<DeviceNo>100000000001</DeviceNo>
<DeviceType>1</DeviceType>
</Device>
</Body>
<ns3:Signature>
<ns3:SignedInfo>
<ns3:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ns3:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ns3:Reference URI="">
<ns3:Transforms>
<ns3:Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">
<ns3:XPath xmlns:ns3="http://www.w3.org/2002/06/xmldsig-filter2" Filter="intersect">//*[local-name()='Header' or local-name()='Body']</ns3:XPath>
</ns3:Transform>
</ns3:Transforms>
<ns3:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ns3:DigestValue>Q98K9JWULpTG9cBXMKPIdL0lg8f16r7GlK6uerOsztM=</ns3:DigestValue>
</ns3:Reference>
</ns3:SignedInfo>
<ns3:SignatureValue>Lneio8EOWK6wIQtNy9jttFh6eXyk7h0kzuytA33r3ALSeO/cg79PpNyXEONgKqJN+fDd0t0endFN mp57nxNLoZiP5GLm3pVGbIv9K8+NQgjO1INk2b0NO/yYlf/lshX8ux8vP3qS5pN3zmEhG5nNFZtE cLLh5z4hJDCb8DNdxIrfwUi/RLAZoUJX+j3eA0QSs69qioH5SbHRozaXfRFyMuI5um6qG2krCNzD ef6qGih/CDOMS4A0tXSBiPqn/ibg6jZDIlHebaw8VV9iDXQtUNkW29jZxaL25/zQLOR4RnvNXI/E ChACumvk3RvYJEdv3kYozqxVZlaHFr0hXT6KIQ==</ns3:SignatureValue>
<ns3:KeyInfo>
<ns3:X509Data>
<ns3:X509Certificate>MIIDgDCCAmgCCQD8woqWbwfU4jANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCY24xEDAOBgNV BAgMB3NpY2h1YW4xEDAOBgNVBAcMB2NoZW5nZHUxDjAMBgNVBAoMBWtvbGJlMQ4wDAYDVQQLDAVr b2xiZTEOMAwGA1UEAwwFa29sYmUxHjAcBgkqhkiG9w0BCQEWD2tvbGJlQGVtYWlsLmNvbTAeFw0x NzEyMTQwODU2MjJaFw0xODAxMTMwODU2MjJaMIGBMQswCQYDVQQGEwJjbjEQMA4GA1UECAwHc2lj aHVhbjEQMA4GA1UEBwwHY2hlbmdkdTEOMAwGA1UECgwFa29sYmUxDjAMBgNVBAsMBWtvbGJlMQ4w DAYDVQQDDAVrb2xiZTEeMBwGCSqGSIb3DQEJARYPa29sYmVAZW1haWwuY29tMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm9j6WBIwrz9r0tL1fgvEAsmYreyjfKgkEZf0PAl85MvI4Y+o 9hCKRcw3GV0o/W52jEMxYuxSQpJuidzyo9KgEdUnwKa8FWvPoH0CsAMdIAwB0H8Iro0T+FVEcLtH ZPwIoXC0P3EGzkWgVPSkaDM9aml3+oG/FQXR5/bIDTv+k6CxwZMBuBpTq1n6ZMgtPEX0zZCN5n/t iYby6smrWq/KboMZpovo7SyhfQMZbNAto1pFfq6NXwqH62gReify97GztDYbO3H3jgiheLM9udPK Y4uj/1fnTInO444KoTOrQPNJ/+CrQ0PvK9yOQqKeTaYaBoefhzs8GQ9WcxbDiDm6DQIDAQABMA0G CSqGSIb3DQEBCwUAA4IBAQCCSSMy7Q/KBYy5Fk36AjPhw5BgCotPWsQCMELQNsVE/+660ovDDFpd WW5K3QAdTSFYMROvSdNbAifzLvNEZCFGr+tImz4jBAsh8ADkvQ3dr/puiGmHdon2M4hk3TLPrcQC LTE5rYWDq/LR35D7bqFrzvno0+of1slaguIJQ7RxAioqCcoRIKrs4psI5NbM8Hc0AWYLYQgHfjqR YyWXdhkUqd0Z8cEptG22tKKzVIJyYiX9lX6fcrYIDcETWB9cRKT/4bu0RzRHEYrQxYRWMmH8vV1V v2xEfAF9eICpER3hvFRVjz2vtuyjOcJWb6zZEqF7A4HTgHhPfJH1lpTO0g4o</ns3:X509Certificate>
</ns3:X509Data>
</ns3:KeyInfo>
</ns3:Signature>
</Shipment>


项目中只用到了commons-io包,没引用别的特殊依赖包
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: