Подписание SOAP-сообщения KALKAN-апплетом

Стандартный Калкан-апплет не позволяет подписывать SOAP-сообщения. Допустим, перед вами все-таки встала такая или подобная задача... Теоретически можно расширить апплет, добавив свой метод, но для этого нужно убрать родную подпись апплета и установить свою. Важно! Самоподписанный сертификат для подписания апплета не подойдет, нужен легально подписанный сертификат. Дальшейшая практическая инструкция подразумевает, что у вас есть легальный сертификат для подписи апплета, иначе некоторые браузеры могут не принять самоподписанный сертификат.
  1. Создаем maven-проект. Вот содержимое pom.xml:
    
    
        4.0.0
    
        kz.kesh.blog
        kalkan-applet
        1.2.3
    
        
            
                kz.gov.pki
                knca_applet
                0.1.1
            
        
    
        
            knca_applet_patched
            
                
                    org.apache.maven.plugins
                    maven-compiler-plugin
                    3.2
                    
                        1.7
                        1.7
                        ${project.build.sourceEncoding}
                    
                
                
                    org.apache.maven.plugins
                    maven-shade-plugin
                    2.4.1
                    
                        
                            package
                            
                                shade
                            
                            
                                
                                    
                                        *:*
                                        
                                            META-INF/*.SF
                                            META-INF/*.DSA
                                            META-INF/*.RSA
                                        
                                    
                                
                                
                            
                        
                    
                
                
                    org.apache.maven.plugins
                    maven-jar-plugin
                    2.4
                    
                        
                            false
    
                            
                                false
                                true
                                true
                            
                            
                                *
                                *
                                test@kesh.kz
                                true
                                all-permissions
                                *
                                Kesh blog
                            
                        
                    
                
            
        
    
    
  2. Необходимо добавить в локальный репозиторий maven jar-ку апплета, которую будем прокачивать. Это можно сделать командой:
    mvn install:install-file -Dfile=knca_applet-0.1.1.jar -DgroupId=kz.gov.pki -DartifactId=knca_applet -Dversion=0.1.1 -Dpackaging=jar
    
    У вас должен быть в наличии файл knca_applet-0.1.1.jar и он должен находиться в текущей директории, откуда выполняется вышеуказанная команда.
  3. Далее необходимо создать пакет kz.gov.pki.knca.applet и разместить в нем наш новый класс SuperKalkanApplet:
    package kz.gov.pki.knca.applet;
    
    import kz.gov.pki.kalkan.Storage;
    import kz.gov.pki.kalkan.jce.provider.KalkanProvider;
    import kz.gov.pki.knca.applet.exception.AECodes;
    import kz.gov.pki.knca.applet.exception.AppletException;
    import kz.gov.pki.knca.applet.utils.KeyStoreUtil;
    import org.apache.xml.security.signature.XMLSignature;
    import org.apache.xml.security.transforms.Transforms;
    import org.w3c.dom.Attr;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import org.xml.sax.InputSource;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.transform.OutputKeys;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    import java.io.FileInputStream;
    import java.io.StringReader;
    import java.io.StringWriter;
    import java.security.AccessController;
    import java.security.KeyStore;
    import java.security.PrivateKey;
    import java.security.PrivilegedAction;
    import java.security.cert.X509Certificate;
    
    public class SuperKalkanApplet extends MainApplet{
    
    	@SuppressWarnings("unused")
    	public ResultWrapper signSoap(final String storageName,final String storagePath,final String alias,final String password,final String xml){
    		return AccessController.doPrivileged(
    				new PrivilegedAction() {
    					public ResultWrapper run() {
    						try {
    							Document doc = xmlToDoc(xml);
    							Node envelope = doc.getFirstChild();
    							Node header = null;
    							Node body = null;
    							for (int i = 0; i < envelope.getChildNodes().getLength(); i++) {
    								Node node = envelope.getChildNodes().item(i);
    								if ("Header".equals(node.getLocalName())) {
    									header = node;
    								} else if ("Body".equals(node.getLocalName())) {
    									body = node;
    								}
    							}
    							if (header == null) {
    								header = doc.createElement(envelope.getPrefix() + ":Header");
    								envelope.insertBefore(header, body);
    							}
    							if (body == null) throw new RuntimeException("Body not found");
    							Attr attr = doc.createAttribute("id");
    							attr.setValue("body");
    							body.getAttributes().setNamedItem(attr);
    							((Element) body).setIdAttributeNode(attr, true);
    
    							Storage storage = getStorage(storageName);
    							KeyStore keyStore = KeyStoreUtil.getKeyStore(storage, storagePath, password.toCharArray(), getProvider());
    
    							PrivateKey key = KeyStoreUtil.getPrivateKey(storage, storagePath, alias, password.toCharArray(), getProvider());
    
    							X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
    							String signMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
    							String digestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    							if(!"RSA".equals(key.getAlgorithm())){
    								signMethod = "http://www.w3.org/2001/04/xmldsig-more#gost34310-gost34311";
    								digestMethod = "http://www.w3.org/2001/04/xmldsig-more#gost34311";
    							}
    							XMLSignature signature = new XMLSignature(doc, "", signMethod);
    							Transforms transforms = new Transforms(doc);
    							transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
    							transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
    							signature.addDocument("#body", transforms, digestMethod);
    							signature.addKeyInfo(certificate);
    							NodeList nodes = header.getChildNodes();
    							if (nodes.getLength() > 0) {
    								Node node = header.getFirstChild();
    								header.insertBefore(signature.getElement(), node);
    							} else {
    								header.appendChild(signature.getElement());
    							}
    							signature.sign(key);
    							ResultWrapper rw = new ResultWrapper();
    							String signedXml = docToText(doc);
    							System.out.println(signedXml);
    							rw.setResult(signedXml);
    							return rw;
    						} catch (Exception e) {
    							System.err.println("Error: " + e.getMessage());
    							e.printStackTrace();
    							return new ResultWrapper(e.getMessage());
    						}
    					}
    				}
    		);
    	}
    
    	private static Document xmlToDoc(String xmlStr) {
    		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    		factory.setNamespaceAware(true);
    		DocumentBuilder builder;
    		try
    		{
    			builder = factory.newDocumentBuilder();
    			return builder.parse( new InputSource( new StringReader( xmlStr ) ) );
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    	private static String docToText(Document document) throws Exception {
    		TransformerFactory tf = TransformerFactory.newInstance();
    		Transformer transformer = tf.newTransformer();
    		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"yes");
    		StringWriter writer = new StringWriter();
    		transformer.transform(new DOMSource(document),new StreamResult(writer));
    		return writer.getBuffer().toString();
    	}
    
    	private Storage getStorage(String storageName) throws AppletException {
    		Storage storage = Storage.get(storageName);
    		if(storage == null) {
    			System.err.println("Unknown storage name : " + storageName);
    			throw new AppletException(AECodes.UNKNOWN_STORAGE.toString());
    		} else {
    			return storage;
    		}
    	}
    }
    
  4. Теперь необходимо собрать новый jar-файл:
    mvn clean package
    
Сигнатура нового метода следующая
ResultWrapper signSoap(final String storagePath,final String alias,final String password,final String xml)
storagePath - путь к подписи alias - идентификатор сертификата password - пароль xml - SOAP-сообщение для подписи При внедрении апплета в html нужно указать knca_applet_patched.jar (или ваше название) и kz.gov.pki.knca.applet.SuperKalkanApplet (тоже можно изменить).
Поделиться данной статьей через:  

2 комментария

  1. Olzhas:

    Здравствуйте!

    Я был бы очень рад если вы можете уделить мне пару минут и ответить на эти жизненно важные вопросы для меня.

    Я начинающий программист, и web-программирования для меня слишком далеко. Я пишу лабораторную работу, где у меня десктопное приложение написанное на java. Скачал sdk kalkan, добавил в свой проект, но не понимаю куда дальше идти.

    Суть лабораторной работы: Есть некоторый документ, его нужно подписать.
    Интерфейс в принципе самый простой : Button, FileChooser, и Флаг(true-когда проверен, false- если нет).

    Хочу узнать как используя библиотеку можна написать такое простое приложение.

    Заранее спасибо за уделенное мне время.

Добавить комментарий для Naik Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.