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包,没引用别的特殊依赖包
相关文章推荐
- [JAVA] MyEclipse的XDoclet生成hbm文件
- java中利用类名生成Class对象,通过class对象创建实体类的理解
- 用Java生成文本文件(收集)
- java生成excel
- java生成pdf文件的完善
- Java自动生成mybatis更新sql语句
- 2014-12-01-1717-Java-随机生成8位数(100以内)的数组
- java&nbsp;&amp;&nbsp;javascript&nbsp;生成随机数
- 你不可错过的二维码生成与解析-java后台与前端js都有
- Java中用Apache POI生成excel和word文档
- 生成MyEclipse6.5&7.5&8.0注册码的java源码 生成MyEclipse6.5&7.5&8.0注册码的java源码 MyEclipseKeyGen MyEclipse6.5&7.5&
- Java图形界面编程生成exe文件
- Java 动态生成 复杂 .doc文件
- java指定范围内生成随机数
- 用java生成word文档
- 关于Java生成指定格式的xml文档
- 关于打包java文件并生成可执行文件的问题
- 杨小杨 数据库生成javabean小工具(java)
- 用java代码根据html页面生成图片
- java生成可执行jar