-
-
Save vrudikov/b551127281b2f817bbae3690a15c5960 to your computer and use it in GitHub Desktop.
Пример подписывания SOAP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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