对接海关总署电子口岸时遇到清单加密问题的总结

问题描述:

由于海关各个关区的申报都对接了总署,以前向各个关区申报的业务现在全部通过向总署申报,在向总署申报清单时遇到了报文加密问题,怎么加密也加不对。总署给的规则也不清晰,目前是第一家在做这个,所以也没人请教,特发此文用来记录下。

报文格式加密处理代码

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Map;
import *.CertificateInfo;
import *.init.CustSwxaJm;
import *.XmlElement;
/**
 * 

标题: 调用三未信安加密机加密

*

功能: 提供对海关总署报文加密的加密服务

*/ public class CustSwxaJmService { private final CustSwxaJm swxaJm; private final CertificateInfo certificateInfo; public CustSwxaJmService(Map envParams) { this.swxaJm = new CustSwxaJm(envParams); this.certificateInfo = new CertificateInfo(envParams); } /** * @param envParams * @param index 密钥索引位置 (1:南沙,2:黄埔) */ public CustSwxaJmService(Map envParams, int index) { this.swxaJm = new CustSwxaJm(envParams, index); this.certificateInfo = new CertificateInfo(envParams, index); } /** * 对加密节点进行预处理 * @param envParams * @param ceb621Message * @param encodeStr */ public void setSignatureXmlElement(XmlElement ceb621Message, String encodeStr) { String digestValueZY = swxaJm.getHash_SHAl(toHashText(ceb621Message)); //增加 Signature XmlElement signature = new XmlElement("Signature");//1层 signature.setAttribute("xmlns", "http://www.w3.org/2000/09/xmldsig#"); ceb621Message.addSubElement(signature); //增加 Signature-SignedInfo XmlElement signedInfo = new XmlElement("SignedInfo");//2层 signature.addSubElement(signedInfo); //增加 Signature-SignedInfo-CanonicalizationMethod XmlElement canonicalizationMethod = new XmlElement("CanonicalizationMethod");//3层 canonicalizationMethod.setAttribute("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); signedInfo.addSubElement(canonicalizationMethod); //增加 Signature-SignedInfo-SignatureMethod XmlElement signatureMethod = new XmlElement("SignatureMethod");//3层 signatureMethod.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"); signedInfo.addSubElement(signatureMethod); //增加 Signature-SignedInfo-Reference XmlElement seference = new XmlElement("Reference");//3层 seference.setAttribute("URI", ""); signedInfo.addSubElement(seference); //增加 Signature-SignedInfo-Reference-Transforms XmlElement transforms = new XmlElement("Transforms");//4层 seference.addSubElement(transforms); //增加 Signature-SignedInfo-Reference-Transforms-Transform XmlElement transform = new XmlElement(signedInfo, "Transform");//5层 transform.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature"); transforms.addSubElement(transform); //增加 Signature-SignedInfo-Reference-DigestMethod XmlElement digestMethod = new XmlElement("DigestMethod");//4层 digestMethod.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1"); seference.addSubElement(digestMethod); //增加 Signature-SignedInfo-Reference-DigestValue XmlElement digestValue = new XmlElement("DigestValue");//4层 //String digestValueJQ = "digestValue加签值"; digestValue.setValue(digestValueZY); seference.addSubElement(digestValue); //增加 Signature-SignatureValue XmlElement signatureValue = new XmlElement("SignatureValue");//2层 signature.addSubElement(signatureValue); String signatureValueJQ = swxaJm.getPrivateSign(toXmlText(ceb621Message, encodeStr));//getXMLStr(signedInfo, 2) //signatureValueJQ.replace(" ", "\n"); signatureValue.setValue(signatureValueJQ); //增加 Signature-KeyInfo XmlElement keyInfo = new XmlElement("KeyInfo");//2层 signature.addSubElement(keyInfo); //增加 Signature-KeyInfo-KeyName XmlElement keyName = new XmlElement("KeyName");//3层 String keyNameJQ = certificateInfo.getSerialNumber(); keyName.setValue(keyNameJQ); keyInfo.addSubElement(keyName); //增加 Signature-KeyInfo-X509Data XmlElement x509Data = new XmlElement("X509Data");//3层 keyInfo.addSubElement(x509Data); //增加 Signature-KeyInfo-X509Data-X509Certificate XmlElement x509Certificate = new XmlElement("X509Certificate");//4层 String x509CertificateJQ = certificateInfo.getX509InfoBase64(); //x509CertificateJQ.replace(" ", "\n"); x509Certificate.setValue(x509CertificateJQ); x509Data.addSubElement(x509Certificate); } /** * 海关格式化XML内容 * @param xmlElement * @param encodeStr * @return */ public String toXmlText(XmlElement xmlElement, String encodeStr) { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintStream out = new PrintStream(os); String nodeName = "Signature"; //添加头结点 out.print(encodeStr); out.print('\n'); list(xmlElement, out, 0, nodeName); return new String(os.toByteArray()); } private String toHashText(XmlElement xmlElement) { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintStream out = new PrintStream(os); list(xmlElement, out, 0, "Signature"); String xmlTest = new String(os.toByteArray()); return xmlTest; } private void list(XmlElement xmlElement, PrintStream out, int baseIndent, String nodeName) { if (!existParentElement(xmlElement, nodeName)) { for (int i = 0; i < baseIndent; i++) { out.print('\t'); } } out.print("<" + xmlElement.qName); if (xmlElement.localName != null && xmlElement.localName.length() > 0) { out.print(" name=\"" + xmlElement.localName + "\""); } for (final String name : xmlElement.getAttributes().keySet()) { out.print(" " + name + "=\"" + XmlElement.xmlEscape(xmlElement.getAttributes().get(name), 0xffff) + "\""); } int nSub = xmlElement.subElements.size(); if (nSub == 0) { if (xmlElement.value != null && xmlElement.value.length() > 0) { out.print(">" + XmlElement.xmlEscape(xmlElement.value, 31) + ""); if (!existParentElement(xmlElement, nodeName)) { out.print('\n'); } } else { out.print("/>"); if (!existParentElement(xmlElement, nodeName)) { out.print('\n'); } } } else { out.print(">"); if (!existParentElement(xmlElement, nodeName)) { out.print('\n'); } if (xmlElement.value != null && xmlElement.value.length() > 0) { if (!existParentElement(xmlElement, nodeName)) { for (int i = 0; i < baseIndent + 1; i++) { out.print('\t'); } } out.print(xmlElement.value); if (!existParentElement(xmlElement, nodeName)) { out.print('\n'); } } for (int i = 0; i < nSub; i++) { list(xmlElement.subElements.get(i), out, baseIndent + 1, nodeName); } for (int i = 0; i < baseIndent; i++) { if (!existParentElement(xmlElement, nodeName)) { out.print('\t'); } } out.print(""); if (!existParentElement(xmlElement, nodeName)) { out.print('\n'); } } } /** * 判断父节点中是否存在nodeName节点 * @param xmlElement 节点 * @param nodeName 比较节点 * @return */ private boolean existParentElement(XmlElement xmlElement, String nodeName) { boolean flag = false; String name = xmlElement.qName; if (nodeName.equals(name)) { flag = true; } else { XmlElement pNode = xmlElement.getParentNode(); if (pNode != null) { String pname = pNode.qName; if (nodeName.equals(pname)) { flag = true; } else { flag = existParentElement(pNode, nodeName); } } } return flag; } }
获取证书的代码

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Map;
import org.apache.xml.security.utils.Base64;
/**
 * 

标题: 证书信息

*

功能: 获取证书信息

*/ public class CertificateInfo { /**系统环境变量*/ private final Map envParams; /**证书地址*/ private String x509CertFilePath; /**证书*/ private X509Certificate x509Certificate; /**证书编号*/ private String SerialNumber; /**证书信息Base64编码*/ private String x509InfoBase64; public CertificateInfo(Map envParams) { this.envParams = envParams; init(1); } public CertificateInfo(Map envParams, int index) { this.envParams = envParams; init(index); } private void init(int index) { setX509CertFilePath(initX509CertFilePath(index)); setX509Certificate(initX509Certificate()); setSerialNumber(initSerialNumber()); setX509InfoBase64(initX509InfoBase64()); } /** * 证书地址 * @return */ public String initX509CertFilePath(int index) { switch (index) { case 1: return CustSysOptions.getCertificateAddress(this.envParams); case 3: return CustSysOptions.getCertificateAddress_A(this.envParams); } return null; } /** * 获取证书 * @return */ private X509Certificate initX509Certificate() { X509Certificate x509Cert = null; FileInputStream in = null; try { in = new FileInputStream(getX509CertFilePath()); CertificateFactory cf = CertificateFactory.getInstance("X.509"); x509Cert = (X509Certificate) cf.generateCertificate(in); } catch (FileNotFoundException e) { throw new RuntimeException("证书文件不存在,文件路径:" + getX509CertFilePath(), e); } catch (CertificateException e) { throw new RuntimeException("获取证书内容失败!", e); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { throw new RuntimeException("关闭读取证书的文件流失败!", e); } } return x509Cert; } /** * 获取证书编号 * @return * 赵力 */ private String initSerialNumber() { String numberStr = getX509Certificate().getSerialNumber().toString(16); if (StrUtil.isNotNull(numberStr) && numberStr.length() < 16) { StringBuilder buf = new StringBuilder(); int num = 16 - numberStr.length(); for (int i = 0; i < num; i++) { buf.append("0"); } numberStr = buf.toString() + numberStr; } return numberStr; } /** * 获取证书信息BASE64编码 * @return * 赵力 */ private String initX509InfoBase64() { try { return Base64.encode(x509Certificate.getEncoded()); } catch (CertificateEncodingException e) { throw new RuntimeException("获取证书信息BASE64编码失败", e); } } public String getX509CertFilePath() { return x509CertFilePath; } private void setX509CertFilePath(String x509CertFilePath) { this.x509CertFilePath = x509CertFilePath; } public X509Certificate getX509Certificate() { return x509Certificate; } private void setX509Certificate(X509Certificate x509Certificate) { this.x509Certificate = x509Certificate; } public String getSerialNumber() { return SerialNumber; } private void setSerialNumber(String serialNumber) { SerialNumber = serialNumber; } public String getX509InfoBase64() { return x509InfoBase64; } private void setX509InfoBase64(String x509InfoBase64) { this.x509InfoBase64 = x509InfoBase64; } }
三未信安加密对象代码

import java.io.IOException;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.utils.Base64;
import org.apache.xml.security.utils.IgnoreAllErrorHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.sansec.jce.provider.SwxaProvider;
/**
 * 

标题:三未信安加密对象

*

功能:对发送总署的报文进行加密

*/ public class CustSwxaJm { /**配置文件地址*/ private String swxaConfigFilePath; /**内部密钥位置*/ private int keyIndex; /**内部密钥*/ private KeyPair inKeyPair; /**外部密钥*/ private KeyPair outKeyPair; public CustSwxaJm(Map envParams) { init(envParams, 1); } public CustSwxaJm(Map envParams, int keyIndex) { init(envParams, keyIndex); } /** * 初始化加密对象 */ private void init(Map envParams, int keyIndex) { setSwxaConfigFilePath(StrUtil.obj2str(DataConfig.getDataConfig(envParams, "swxaConfigFilePath"))); initProvider(); setKeyIndex(keyIndex); setInKeyPair(initInKeyPair()); setOutKeyPair(initOutKeyPair()); } /** * 初始化配置文件 * 功能:加载配置文件并进行初始化 */ private void initProvider() { SwxaProvider provider = new SwxaProvider(getSwxaConfigFilePath()); Security.addProvider(provider); } /** * 生成内部密钥 */ private KeyPair initInKeyPair() { KeyPair kp = null; try { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SwxaJCE"); kpg.initialize(keyIndex << 16); kp = kpg.genKeyPair(); if (kp == null) { throw new RuntimeException(); } } catch (Exception e) { throw new RuntimeException("获取内部密钥失败"); } return kp; } /** * 生成外部密钥 */ private static KeyPair initOutKeyPair() { KeyPair kp = null; try { int keylength = 1024; KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SwxaJCE"); kpg.initialize(keylength); kp = kpg.genKeyPair(); if (kp == null) { throw new RuntimeException(); } } catch (Exception e) { throw new RuntimeException("生成外部密钥失败"); } return kp; } /** * 生成HASH摘要 */ public String getHash_SHAl(String xmlInfo) { byte[] plain_bytes; try { Init.init(); Canonicalizer canonicalizer = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS); plain_bytes = canonicalizer.canonicalize(xmlInfo.getBytes()); } catch (RuntimeException e) { throw new RuntimeException("报错内容", e); } catch (CanonicalizationException e) { throw new RuntimeException("报错内容", e); } catch (ParserConfigurationException e) { throw new RuntimeException("报错内容", e); } catch (IOException e) { throw new RuntimeException("报错内容", e); } catch (SAXException e) { throw new RuntimeException("报错内容", e); } catch (InvalidCanonicalizerException e) { throw new RuntimeException("报错内容", e); } String hashShal; MessageDigest md; try { md = MessageDigest.getInstance("SHA1", "SwxaJCE"); md.update(plain_bytes); byte[] data = md.digest(); hashShal = Base64.encode(data); } catch (Exception e) { throw new RuntimeException("加密机生成hash摘要失败"); } return hashShal; } /** * 进行加签 * @param strSignedInfo XML报文不包含头的文本 */ public String getPrivateSign(String xmlText) { String privateSignatue; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(true); byte[] bytes = null; try { DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(new IgnoreAllErrorHandler()); //signedInfo Document doc = builder.parse(new InputSource(new StringReader(xmlText))); NodeList nodeList = doc.getElementsByTagName("SignedInfo"); Node node = nodeList.item(0); Init.init(); Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS); //格式化 bytes = canon.canonicalizeSubtree(node); PrivateKey privateKey = getInKeyPair().getPrivate(); Signature signatue = null; signatue = Signature.getInstance("SHA1WithRSA", "SwxaJCE"); signatue.initSign(privateKey); signatue.update(bytes); privateSignatue = Base64.encode(signatue.sign()); } catch (Exception e) { throw new RuntimeException("加密机进行内部签名失败"); } return privateSignatue; } private String getSwxaConfigFilePath() { return swxaConfigFilePath; } private void setSwxaConfigFilePath(String swxaConfigFilePath) { this.swxaConfigFilePath = swxaConfigFilePath; } public KeyPair getInKeyPair() { return inKeyPair; } private void setInKeyPair(KeyPair inKeyPair) { this.inKeyPair = inKeyPair; } public int getKeyIndex() { return keyIndex; } private void setKeyIndex(int keyIndex) { this.keyIndex = keyIndex; } public KeyPair getOutKeyPair() { return outKeyPair; } private void setOutKeyPair(KeyPair outKeyPair) { this.outKeyPair = outKeyPair; }
}

实际调用

package snsoft.customs.custentryelist.serv;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 

标题: 海关总署进境清单导出监听

*/ public class CustentryelistZSXmlListener { protected final String DATATYPE1 = "YYYYMMDDhhmmss"; protected final String DATATYPE2 = "YYYYMMDD"; private static final String MTABLE = "custentryelist"; private final CustSwxaJmService swxaJmService; /** * @param envParams * @param parameter * @param xmlObj */ public CustentryelistZSXmlListener(Map envParams, Map parameter, XmlExObject xmlObj) { super(envParams, parameter, xmlObj); this.swxaJmService = new CustSwxaJmService(envParams); } /** * 加密对节点进行预处理 */ @Override public void onXmlElementCreated(XmlElement xmlElement) { super.onXmlElementCreated(xmlElement); XmlElement ceb621Message = xmlElement.getSubElement("ceb:CEB621Message"); String encodeStr = xmlObj.getXmlencoding(); swxaJmService.setSignatureXmlElement(ceb621Message, encodeStr); } /** * 海关格式化xml内容 */ @Override public String toXml(XmlElement xmlElement) { XmlElement ceb621Message = xmlElement.getSubElement("ceb:CEB621Message"); String encodeStr = xmlObj.getXmlencoding(); return swxaJmService.toXmlText(ceb621Message, encodeStr); } }






你可能感兴趣的:(JAVA)