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

问题描述:

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

报文格式加密处理代码

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Map;
import *.CertificateInfo;
import *.init.CustSwxaJm;
import *.XmlElement;
/**
 * <p>标题: 调用三未信安加密机加密</p>
 * <p>功能: 提供对海关总署报文加密的加密服务 </p>
 */
public class CustSwxaJmService
{
	private final CustSwxaJm		swxaJm;
	private final CertificateInfo	certificateInfo;

	public CustSwxaJmService(Map<String,Object> envParams)
	{
		this.swxaJm = new CustSwxaJm(envParams);
		this.certificateInfo = new CertificateInfo(envParams);
	}

	/**
	 * @param envParams
	 * @param index 密钥索引位置  (1:南沙,2:黄埔)
	 */
	public CustSwxaJmService(Map<String,Object> 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) + "</" + xmlElement.qName + ">");
				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("</" + xmlElement.qName + ">");
			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;
/**
 * <p>标题: 证书信息</p>
 * <p>功能: 获取证书信息</p>
 */
public class CertificateInfo
{
	/**系统环境变量*/
	private final Map<String,Object>	envParams;
	/**证书地址*/
	private String						x509CertFilePath;
	/**证书*/
	private X509Certificate				x509Certificate;
	/**证书编号*/
	private String						SerialNumber;
	/**证书信息Base64编码*/
	private String						x509InfoBase64;

	public CertificateInfo(Map<String,Object> envParams)
	{
		this.envParams = envParams;
		init(1);
	}

	public CertificateInfo(Map<String,Object> 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;
/**
 * <p>标题:三未信安加密对象 </p>
 * <p>功能:对发送总署的报文进行加密 </p>
 */
public class CustSwxaJm
{
	/**配置文件地址*/
	private String	swxaConfigFilePath;
	/**内部密钥位置*/
	private int		keyIndex;
	/**内部密钥*/
	private KeyPair	inKeyPair;
	/**外部密钥*/
	private KeyPair	outKeyPair;

	public CustSwxaJm(Map<String,Object> envParams)
	{
		init(envParams, 1);
	}

	public CustSwxaJm(Map<String,Object> envParams, int keyIndex)
	{
		init(envParams, keyIndex);
	}

	/**
	* 初始化加密对象
	*/
	private void init(Map<String,Object> 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;
/**
 * <p>标题: 海关总署进境清单导出监听</p>
 */
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<String,Object> envParams, Map<String,Object> 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);
	}

	
}






  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值