Skip to content

Instantly share code, notes, and snippets.

@asilchev
Created September 18, 2012 11:41
Show Gist options
  • Save asilchev/3742724 to your computer and use it in GitHub Desktop.
Save asilchev/3742724 to your computer and use it in GitHub Desktop.
Пример подписывания SOAP
package ru.cti.crypto;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
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.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.transform.stream.StreamSource;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xpath.XPathAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import ru.CryptoPro.JCPxml.xmldsig.JCPXMLDSigInit;
public class SoapSign {
private static Logger logger = LoggerFactory.getLogger(SoapSign.class);
private static final String PASSWORD = "qweasd123";
private static final String ALIAS = "gosuslugi-container";
public static void main(String[] args) throws Exception {
logger.debug("application start...");
if(!JCPXMLDSigInit.isInitialized()) {
JCPXMLDSigInit.init();
}
KeyStore ks = KeyStore.getInstance("HDImageStore");
ks.load(null, null);
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
String alias = aliases.nextElement();
logger.debug("found alias: {}", alias);
}
//Получение ключа и сертификата
PrivateKey privateKey = (PrivateKey)ks.getKey("gosuslugi-container", PASSWORD.toCharArray());
X509Certificate cert = (X509Certificate)ks.getCertificate(ALIAS);
logger.debug("key: {}, certificate: {}", new Object[] {privateKey, cert});
// Подготовка сообщения: в данном случае — это чтение сообщения из файла message.xml в кодировке UTF-8.
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage message = mf.createMessage();
SOAPPart soapPart = message.getSOAPPart();
FileInputStream is = new FileInputStream("target/classes/message.xml");
soapPart.setContent(new StreamSource(is));
message.getSOAPPart().getEnvelope().addNamespaceDeclaration("ds", "http://www.w3.org/2000/09/xmldsig#");
Document doc = message.getSOAPPart().getEnvelope().getOwnerDocument();
//Добавляем заголовки для помещения информации о подписи:
WSSecHeader header = new WSSecHeader();
header.setActor("http://smev.gosuslugi.ru/actors/smev");
header.setMustUnderstand(false);
header.insertSecurityHeader(message.getSOAPPart().getEnvelope().getOwnerDocument());
// Элемент подписи.
Element token = header.getSecurityHeader();
logger.debug("token: {}", token);
// Загрузка провайдера.
Provider xmlDSigProvider = new ru.CryptoPro.JCPxml.dsig.internal.dom.XMLDSigRI();
logger.debug("xmlDSigProvider: {}", xmlDSigProvider);
//Добавляем описание преобразований над документом и узлом SignedInfo согласно методическим рекомендациям СМЭВ.
final Transforms transforms = new Transforms(doc);
transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", xmlDSigProvider);
//Преобразования над узлом ds:SignedInfo:
List<Transform> transformList = new ArrayList<Transform>();
Transform transformC14N = fac.newTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (XMLStructure) null);
transformList.add(transformC14N);
// Ссылка на подписываемые данные с алгоритмом хеширования ГОСТ 34.11.
Reference ref = fac.newReference("#body", fac.newDigestMethod("http://www.w3.org/2001/04/xmldsig-more#gostr3411", null),
transformList, null, null);
//Задаём алгоритм подписи:
SignedInfo si = fac.newSignedInfo( fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE,
(C14NMethodParameterSpec) null), fac.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411", null), Collections.singletonList(ref));
//Создаём узел ds:KeyInfo с информацией о сертификате:
KeyInfoFactory kif = fac.getKeyInfoFactory();
X509Data x509d = kif.newX509Data(Collections.singletonList((X509Certificate) cert));
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(x509d));
//Подписываем данные в элементе token:
javax.xml.crypto.dsig.XMLSignature sig = fac.newXMLSignature(si, ki);
DOMSignContext signContext = new DOMSignContext((Key) privateKey, token);
sig.sign(signContext);
//Следующий этап — поместить узел ds:Signature и сертификат (X509Certificate) в узел wsse:Security,
//причём сертификат нужно удалить из ds:KeyInfo и оставить там ссылку на wsse:BinarySecurityToken с сертификатом:
// Узел подписи Signature.
Element sigE = (Element) XPathAPI.selectSingleNode(signContext.getParent(), "//ds:Signature");
// Блок данных KeyInfo.
Node keyE = XPathAPI.selectSingleNode(sigE, "//ds:KeyInfo", sigE);
// Элемент SenderCertificate, который должен содержать сертификат.
Element cerVal = (Element) XPathAPI.selectSingleNode(token, "//*[@wsu:Id='SenderCertificate']");
cerVal.setTextContent(XPathAPI.selectSingleNode(keyE, "//ds:X509Certificate", keyE).getFirstChild().getNodeValue());
// Удаляем содержимое KeyInfo
keyE.removeChild(XPathAPI.selectSingleNode(keyE, "//ds:X509Data", keyE));
logger.debug("keyE:{}" , keyE.getNodeName());
NodeList chl = keyE.getChildNodes();
for (int i = 0; i < chl.getLength(); i++) {
keyE.removeChild(chl.item(i));
}
// Узел KeyInfo содержит указание на проверку подписи с помощью сертификата SenderCertificate.
Node str = keyE.appendChild(doc.createElementNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "wsse:SecurityTokenReference"));
Element strRef = (Element)str.appendChild(doc.createElementNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "wsse:Reference"));
strRef.setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
strRef.setAttribute("URI", "#SenderCertificate");
header.getSecurityHeader().appendChild(sigE);
ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
String strMsg = new String(out.toByteArray());
logger.debug("val: {}", strMsg );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment