问题描述:
由于海关各个关区的申报都对接了总署,以前向各个关区申报的业务现在全部通过向总署申报,在向总署申报清单时遇到了报文加密问题,怎么加密也加不对。总署给的规则也不清晰,目前是第一家在做这个,所以也没人请教,特发此文用来记录下。
报文格式加密处理代码
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) + "" + 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;
/**
* 标题: 证书信息
* 功能: 获取证书信息
*/
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);
}
}