├── keystore ├── truststore ├── .gitignore ├── client_1.11 ├── readme.txt ├── client │ ├── src │ │ ├── test │ │ │ └── resources │ │ │ │ ├── integration_tests.properties │ │ │ │ ├── smev-gar-service.xsd │ │ │ │ └── smev-iemk-eln-service.xsd │ │ └── main │ │ │ ├── java │ │ │ └── ru │ │ │ │ └── voskhod │ │ │ │ └── smev │ │ │ │ └── message_exchange_service_client │ │ │ │ ├── datatypes │ │ │ │ ├── MessageTypeEnum.java │ │ │ │ ├── QueueStatistics.java │ │ │ │ └── MessageMetaDataAndSMEVSignature.java │ │ │ │ ├── WebServiceClientException.java │ │ │ │ ├── PersonalSigner.java │ │ │ │ ├── impl │ │ │ │ ├── PersonalSignerImpl.java │ │ │ │ ├── FileAttachmentImpl.java │ │ │ │ ├── InAttachments.java │ │ │ │ ├── AttachmentDataSourceImpl.java │ │ │ │ ├── KeyPersonalSignerImpl.java │ │ │ │ ├── FileSystemSMEVCertificateStore.java │ │ │ │ ├── SmallOutAttachment.java │ │ │ │ ├── AbstractSMEVCertificateStore.java │ │ │ │ ├── LargeOutAttachment.java │ │ │ │ └── AttachmentBuilder.java │ │ │ │ ├── ClientSideProcessingException.java │ │ │ │ ├── DigestResult.java │ │ │ │ ├── util │ │ │ │ ├── AttachInputStream.java │ │ │ │ └── PipeOutputStream.java │ │ │ │ ├── OutAttachment.java │ │ │ │ ├── intercept │ │ │ │ ├── InterceptorTubeFactory.java │ │ │ │ ├── InterceptorStorage.java │ │ │ │ ├── InterceptorTube.java │ │ │ │ └── CustomByteArrayOutputStream.java │ │ │ │ ├── SomeMimeTypes.java │ │ │ │ ├── SMEVCertificateStore.java │ │ │ │ ├── InAttachment.java │ │ │ │ ├── SignatureOperationsClient.java │ │ │ │ ├── BusinessProcessMetadataBuilder.java │ │ │ │ └── MessageExchangeHelper.java │ │ │ └── resources │ │ │ └── META-INF │ │ │ └── metro.xml │ ├── example │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── Example.java │ ├── xml-artifacts │ │ ├── smev-iemk-eln-service.xsd │ │ ├── smev-business-process-metadata-1.0.xsd │ │ └── transport-service │ │ │ └── 1.1 │ │ │ ├── smev-message-exchange-faults-1.1.xsd │ │ │ └── smev-message-exchange-basic-1.1.xsd │ └── pom.xml └── crypto │ ├── src │ ├── main │ │ └── java │ │ │ └── ru │ │ │ └── voskhod │ │ │ └── crypto │ │ │ ├── exceptions │ │ │ ├── DocumentIsNotSignedException.java │ │ │ ├── SignatureValidationException.java │ │ │ ├── CertificateRepositoryException.java │ │ │ ├── SignatureProcessingException.java │ │ │ └── SigLibInitializationException.java │ │ │ ├── KeyStoreWrapper.java │ │ │ ├── impl │ │ │ ├── SignatureNamespaceContext.java │ │ │ ├── ValidationResult.java │ │ │ ├── jcp │ │ │ │ └── KeyStoreWrapperJCP.java │ │ │ ├── CacheOptions.java │ │ │ ├── JCPCMSTools.java │ │ │ ├── csp_tj │ │ │ │ └── TrustedKeyStoreWrapperCSP.java │ │ │ ├── CachingKeyStoreWrapper.java │ │ │ ├── PKCS7Tools.java │ │ │ └── SmevTransformSpi.java │ │ │ ├── PipeInputStream.java │ │ │ ├── XMLTransformHelper.java │ │ │ ├── DigitalSignatureFactory.java │ │ │ └── DigitalSignatureProcessor.java │ └── test │ │ └── java │ │ └── ru │ │ └── voskhod │ │ └── crypto │ │ └── Main.java │ └── pom.xml ├── ~$струкция по установке клиента СМЭВ 3.docx └── Инструкция по установке клиента СМЭВ 3.docx /keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimOK/smev3cxf/HEAD/keystore -------------------------------------------------------------------------------- /truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimOK/smev3cxf/HEAD/truststore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | rebel.xml 4 | rebel-remote.xml 5 | target 6 | -------------------------------------------------------------------------------- /client_1.11/readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimOK/smev3cxf/HEAD/client_1.11/readme.txt -------------------------------------------------------------------------------- /~$струкция по установке клиента СМЭВ 3.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimOK/smev3cxf/HEAD/~$струкция по установке клиента СМЭВ 3.docx -------------------------------------------------------------------------------- /Инструкция по установке клиента СМЭВ 3.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimOK/smev3cxf/HEAD/Инструкция по установке клиента СМЭВ 3.docx -------------------------------------------------------------------------------- /client_1.11/client/src/test/resources/integration_tests.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimOK/smev3cxf/HEAD/client_1.11/client/src/test/resources/integration_tests.properties -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/datatypes/MessageTypeEnum.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.datatypes; 2 | 3 | public enum MessageTypeEnum { 4 | REQUEST, RESPONSE, CANCEL 5 | } 6 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/WebServiceClientException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | public final class WebServiceClientException extends Exception { 4 | 5 | public WebServiceClientException(Throwable cause) { 6 | super(cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/PersonalSigner.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import org.w3c.dom.Element; 4 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 5 | 6 | public interface PersonalSigner { 7 | 8 | Element getSignature(Element businessContent) throws SignatureProcessingException; 9 | } 10 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/exceptions/DocumentIsNotSignedException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.exceptions; 2 | 3 | public class DocumentIsNotSignedException extends Exception { 4 | 5 | private static final long serialVersionUID = -4790470069490066305L; 6 | 7 | public DocumentIsNotSignedException() { 8 | super(); 9 | } 10 | 11 | public DocumentIsNotSignedException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/exceptions/SignatureValidationException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.exceptions; 2 | 3 | public class SignatureValidationException extends Exception { 4 | 5 | private static final long serialVersionUID = -1389448617201522045L; 6 | 7 | public SignatureValidationException(String message) { 8 | super(message); 9 | } 10 | 11 | public SignatureValidationException(Throwable cause) { 12 | super(cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/exceptions/CertificateRepositoryException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.exceptions; 2 | 3 | public class CertificateRepositoryException extends Exception { 4 | 5 | private static final long serialVersionUID = -4609868686143002522L; 6 | 7 | public CertificateRepositoryException(Throwable cause) { 8 | super(cause); 9 | } 10 | 11 | public CertificateRepositoryException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/PersonalSignerImpl.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import org.w3c.dom.Element; 4 | import ru.voskhod.smev.message_exchange_service_client.PersonalSigner; 5 | 6 | public class PersonalSignerImpl implements PersonalSigner { 7 | 8 | private final Element signature; 9 | 10 | public PersonalSignerImpl(Element signature) { 11 | this.signature = signature; 12 | } 13 | 14 | public Element getSignature(Element businessContent) { 15 | return signature; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/exceptions/SignatureProcessingException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.exceptions; 2 | 3 | public class SignatureProcessingException extends Exception { 4 | 5 | private static final long serialVersionUID = -8010336801761140376L; 6 | 7 | public SignatureProcessingException(String message, Throwable cause) { 8 | super(message, cause); 9 | } 10 | 11 | public SignatureProcessingException(String message) { 12 | super(message); 13 | } 14 | 15 | public SignatureProcessingException(Throwable cause) { 16 | super(cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/exceptions/SigLibInitializationException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.exceptions; 2 | 3 | public class SigLibInitializationException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = -4592247998950557205L; 6 | 7 | public SigLibInitializationException(Throwable cause) { 8 | super(cause); 9 | } 10 | 11 | public SigLibInitializationException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public SigLibInitializationException(String message) { 16 | super(message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/datatypes/QueueStatistics.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.datatypes; 2 | 3 | public final class QueueStatistics { 4 | 5 | private final String queueName; 6 | private final long pendingMessagesNumber; 7 | 8 | public QueueStatistics(String queueName, long pendingMessagesNumber) { 9 | this.queueName = queueName; 10 | this.pendingMessagesNumber = pendingMessagesNumber; 11 | } 12 | 13 | public String getQueueName() { 14 | return queueName; 15 | } 16 | 17 | public long getPendingMessagesNumber() { 18 | return pendingMessagesNumber; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/ClientSideProcessingException.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | public final class ClientSideProcessingException extends Exception { 4 | 5 | private static final long serialVersionUID = -4646861090264408031L; 6 | 7 | public ClientSideProcessingException() { 8 | } 9 | 10 | public ClientSideProcessingException(String message) { 11 | super(message); 12 | } 13 | 14 | public ClientSideProcessingException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public ClientSideProcessingException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/KeyStoreWrapper.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto; 2 | 3 | import java.security.KeyStoreException; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.security.PrivateKey; 6 | import java.security.UnrecoverableKeyException; 7 | import java.security.cert.CertificateException; 8 | import java.security.cert.X509Certificate; 9 | 10 | public interface KeyStoreWrapper { 11 | 12 | PrivateKey getPrivateKey(String alias, char[] password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException; 13 | 14 | X509Certificate getX509Certificate(String alias) throws CertificateException, KeyStoreException; 15 | 16 | java.security.KeyStore getKeyStore(); 17 | } 18 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/DigestResult.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import ru.voskhod.crypto.PipeInputStream; 4 | 5 | public class DigestResult { 6 | 7 | private final long dataSize; 8 | private final byte[] dataDigest; 9 | 10 | public DigestResult(long dataSize, byte[] dataDigest) { 11 | this.dataSize = dataSize; 12 | this.dataDigest = dataDigest; 13 | } 14 | 15 | public DigestResult(PipeInputStream digestStream) { 16 | this(digestStream.getSize(), digestStream.getDigest()); 17 | } 18 | 19 | public long getDataSize() { 20 | return dataSize; 21 | } 22 | 23 | public byte[] getDataDigest() { 24 | return dataDigest; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/FileAttachmentImpl.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import ru.voskhod.smev.message_exchange_service_client.InAttachment; 4 | import ru.voskhod.smev.message_exchange_service_client.SomeMimeTypes; 5 | 6 | import java.io.*; 7 | 8 | public final class FileAttachmentImpl extends InAttachment { 9 | 10 | private final File content; 11 | 12 | public FileAttachmentImpl(File content) { 13 | this(content, SomeMimeTypes.guessMimeType(content)); 14 | } 15 | 16 | public FileAttachmentImpl(File content, String mimeType) { 17 | super(mimeType, content.length()); 18 | this.content = content; 19 | } 20 | 21 | public InputStream getInputStream() throws FileNotFoundException { 22 | return new BufferedInputStream(new FileInputStream(content)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/SignatureNamespaceContext.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | import javax.xml.namespace.NamespaceContext; 4 | import java.util.Iterator; 5 | 6 | class SignatureNamespaceContext implements NamespaceContext { 7 | 8 | static final String XMLDSIG_NS = "http://www.w3.org/2000/09/xmldsig#"; 9 | 10 | @Override 11 | public String getNamespaceURI(String prefix) { 12 | if ("ds".equals(prefix)) { 13 | return XMLDSIG_NS; 14 | } else { 15 | return null; 16 | } 17 | } 18 | 19 | @Override 20 | public String getPrefix(String namespaceURI) { 21 | throw new UnsupportedOperationException("Not supported yet."); 22 | } 23 | 24 | @Override 25 | public Iterator getPrefixes(String namespaceURI) { 26 | throw new UnsupportedOperationException("Not supported yet."); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/util/AttachInputStream.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.util; 2 | 3 | import java.io.Closeable; 4 | import java.io.FilterInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | public class AttachInputStream extends FilterInputStream { 9 | 10 | private final Closeable obj; 11 | 12 | public AttachInputStream(Closeable obj, InputStream in) { 13 | super(in); 14 | this.obj = obj; 15 | } 16 | 17 | @Override 18 | public void close() throws IOException { 19 | try { 20 | super.close(); 21 | } catch (IOException ex) { 22 | try { 23 | obj.close(); 24 | } catch (IOException ex2) { 25 | ex.addSuppressed(ex2); 26 | } 27 | throw ex; 28 | } 29 | obj.close(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/InAttachments.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.AttachmentContentList; 4 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.AttachmentHeaderList; 5 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.RefAttachmentHeaderList; 6 | 7 | public final class InAttachments { 8 | 9 | public final AttachmentHeaderList headerList; 10 | public final AttachmentContentList contentList; 11 | public final RefAttachmentHeaderList fsAttachmentsList; 12 | 13 | InAttachments(AttachmentHeaderList headerList, AttachmentContentList contentList, RefAttachmentHeaderList fsAttachmentsList) { 14 | this.headerList = headerList; 15 | this.contentList = contentList; 16 | this.fsAttachmentsList = fsAttachmentsList; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/datatypes/MessageMetaDataAndSMEVSignature.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.datatypes; 2 | 3 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.XMLDSigSignatureType; 4 | import ru.voskhod.smev.message_exchange.autogenerated.types.v1_1.MessageMetadata; 5 | 6 | public class MessageMetaDataAndSMEVSignature { 7 | 8 | private final MessageMetadata messageMetadata; 9 | private final XMLDSigSignatureType smevSignature; 10 | 11 | public MessageMetaDataAndSMEVSignature(MessageMetadata messageMetadata, XMLDSigSignatureType smevSignature) { 12 | this.messageMetadata = messageMetadata; 13 | this.smevSignature = smevSignature; 14 | } 15 | 16 | public MessageMetadata getMessageMetadata() { 17 | return messageMetadata; 18 | } 19 | 20 | public XMLDSigSignatureType getSmevSignature() { 21 | return smevSignature; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/OutAttachment.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public interface OutAttachment { 7 | 8 | interface Content { 9 | 10 | /** 11 | * Блокирующий метод записи содержимого файла в поток 12 | * 13 | * @return true, если весь файл был успешно записан; false, если был вызван метод {@link #cancel()} 14 | */ 15 | boolean retrieve(OutputStream output) throws IOException, ClientSideProcessingException; 16 | 17 | /** 18 | * Может вызываться из другого потока для прерывания скачки файла 19 | */ 20 | void cancel(); 21 | } 22 | 23 | /** 24 | * @return некоторое текстовое описание файла 25 | */ 26 | String getName(); 27 | 28 | String getMimeType(); 29 | 30 | byte[] getSignature(); 31 | 32 | Content getContent(); 33 | } 34 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/AttachmentDataSourceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import ru.voskhod.smev.message_exchange_service_client.InAttachment; 4 | 5 | import javax.activation.DataSource; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | 10 | public final class AttachmentDataSourceImpl implements DataSource { 11 | 12 | private final InAttachment attachment; 13 | 14 | public AttachmentDataSourceImpl(InAttachment attachment) { 15 | this.attachment = attachment; 16 | } 17 | 18 | public String getContentType() { 19 | return attachment.getMimeType(); 20 | } 21 | 22 | public InputStream getInputStream() throws IOException { 23 | return attachment.getInputStream(); 24 | } 25 | 26 | public String getName() { 27 | return null; 28 | } 29 | 30 | public OutputStream getOutputStream() { 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/intercept/InterceptorTubeFactory.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.intercept; 2 | 3 | import com.sun.xml.ws.api.pipe.Tube; 4 | import com.sun.xml.ws.assembler.dev.ClientTubelineAssemblyContext; 5 | import com.sun.xml.ws.assembler.dev.ServerTubelineAssemblyContext; 6 | import com.sun.xml.ws.assembler.dev.TubeFactory; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import javax.xml.ws.WebServiceException; 11 | 12 | public final class InterceptorTubeFactory implements TubeFactory { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(InterceptorTubeFactory.class); 15 | 16 | @Override 17 | public Tube createTube(ClientTubelineAssemblyContext context) throws WebServiceException { 18 | logger.info("Creating client-side interceptor tube"); 19 | return new InterceptorTube(context.getTubelineHead(), true); 20 | } 21 | 22 | @Override 23 | public Tube createTube(ServerTubelineAssemblyContext context) throws WebServiceException { 24 | logger.info("Creating server-side interceptor tube"); 25 | return new InterceptorTube(context.getTubelineHead(), false); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client_1.11/client/src/test/resources/smev-gar-service.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Тестовый вид сведений 9 | 10 | 11 | 12 | 13 | Запрос к тестовому сервису 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Ответ от тестового сервиса 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /client_1.11/client/example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.voskhod.smev 8 | example 9 | 1.0.11 10 | 11 | 12 | 13 | ru.voskhod.smev 14 | smev-client 15 | 1.0.11 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 2.3.2 25 | 26 | 1.7 27 | 1.7 28 | UTF-8 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/ValidationResult.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | import java.security.cert.X509Certificate; 4 | 5 | public class ValidationResult { 6 | 7 | private X509Certificate certificate; 8 | private boolean valid; 9 | private String error; 10 | private Exception exception; 11 | 12 | public X509Certificate getCertificate() { 13 | return certificate; 14 | } 15 | 16 | public void setCertificate(X509Certificate certificate) { 17 | this.certificate = certificate; 18 | } 19 | 20 | public boolean isValid() { 21 | return valid; 22 | } 23 | 24 | public void setValid(boolean valid) { 25 | this.valid = valid; 26 | } 27 | 28 | public String getError() { 29 | return error; 30 | } 31 | 32 | public void setError(String error) { 33 | this.error = error; 34 | } 35 | 36 | public Exception getException() { 37 | return exception; 38 | } 39 | 40 | public void setException(Exception exception) { 41 | this.exception = exception; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "\nValidationResult{" + 47 | "certificate=" + certificate + 48 | ", valid=" + valid + 49 | ", error='" + error + '\'' + 50 | ", exception=" + exception + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/KeyPersonalSignerImpl.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import org.w3c.dom.Element; 4 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 5 | import ru.voskhod.smev.message_exchange_service_client.PersonalSigner; 6 | import ru.voskhod.smev.message_exchange_service_client.SignatureOperationsClient; 7 | 8 | import java.security.PrivateKey; 9 | import java.security.cert.X509Certificate; 10 | 11 | public class KeyPersonalSignerImpl implements PersonalSigner { 12 | 13 | private final SignatureOperationsClient signer; 14 | 15 | public KeyPersonalSignerImpl(SignatureOperationsClient signer) { 16 | this.signer = signer; 17 | } 18 | 19 | public KeyPersonalSignerImpl(PrivateKey privateKey, X509Certificate certificate) { 20 | this(new SignatureOperationsClient(privateKey, certificate)); 21 | } 22 | 23 | public static KeyPersonalSignerImpl create(SignatureOperationsClient signer) { 24 | return signer == null ? null : new KeyPersonalSignerImpl(signer); 25 | } 26 | 27 | public Element getSignature(Element businessContent) throws SignatureProcessingException { 28 | businessContent.setAttribute("Id", "PERSONAL_SIGNATURE"); 29 | businessContent.setIdAttribute("Id", true); 30 | return signer.signXMLDSigDetached(businessContent, null); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/SomeMimeTypes.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import org.apache.tika.Tika; 4 | import org.apache.tika.mime.MimeTypes; 5 | 6 | import java.io.File; 7 | 8 | /** 9 | * Класс для определения MIME типов 10 | */ 11 | public class SomeMimeTypes { 12 | 13 | /** 14 | * Фасад детектора MIME типов 15 | */ 16 | private static Tika tika = new Tika(); 17 | 18 | /** 19 | * Пытается определить MIME тип указанного файла 20 | * 21 | * @param f файл 22 | * @return MIME тип или пустую строку, если не удалось определить тип 23 | */ 24 | public static String guessMimeType(File f) { 25 | try { 26 | String result = tika.detect(f); 27 | return result == null ? "" : result; 28 | } catch (Exception ex) { 29 | return ""; 30 | } 31 | } 32 | 33 | /** 34 | * Возращает наиболее часто используемое расширение файла для указанного MIME типа. 35 | * 36 | * @param mimeType MIME тип файла 37 | * @return расширение или пустую строку, если не удалось определить 38 | */ 39 | public static String guessFileExtension(String mimeType) { 40 | try { 41 | return MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension(); 42 | } catch (Exception ex) { 43 | return ""; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/jcp/KeyStoreWrapperJCP.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl.jcp; 2 | 3 | import ru.voskhod.crypto.KeyStoreWrapper; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.security.*; 7 | import java.security.cert.CertificateException; 8 | import java.security.cert.CertificateFactory; 9 | import java.security.cert.X509Certificate; 10 | 11 | public class KeyStoreWrapperJCP implements KeyStoreWrapper { 12 | 13 | private final KeyStore ks; 14 | 15 | /** 16 | * Загружает хранилище ключей указанного типа. 17 | */ 18 | public KeyStoreWrapperJCP() throws Exception { 19 | ks = KeyStore.getInstance("HDImageStore"); 20 | ks.load(null); 21 | } 22 | 23 | public PrivateKey getPrivateKey(String alias, char[] password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { 24 | return (PrivateKey) ks.getKey(alias, password); 25 | } 26 | 27 | public X509Certificate getX509Certificate(String alias) throws CertificateException, KeyStoreException { 28 | X509Certificate certificate = (X509Certificate) ks.getCertificate(alias); 29 | if (certificate == null) 30 | return null; 31 | return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(certificate.getEncoded())); 32 | } 33 | 34 | public KeyStore getKeyStore() { 35 | return ks; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/SMEVCertificateStore.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import java.io.IOException; 4 | import java.security.cert.CertificateException; 5 | import java.security.cert.X509Certificate; 6 | 7 | /** 8 | * Абстрактный класс - хранилище, в котором лежат сертификаты СМЭВ. 9 | * Эти сертификаты используются, чтобы убеждаться, что ЭП-СМЭВ проставлена действительно СМЭВ. 10 | * Наследники реализуют выборку сертификатов, например, из файловой системы, БД, или KeyStore. 11 | * @author dpryakhin 12 | */ 13 | public abstract class SMEVCertificateStore { 14 | 15 | private static SMEVCertificateStore instance; 16 | 17 | public static SMEVCertificateStore getInstance() { 18 | return instance; 19 | } 20 | 21 | public static void setInstance(SMEVCertificateStore instance) { 22 | SMEVCertificateStore.instance = instance; 23 | } 24 | 25 | /** 26 | * Известен ли такой сертификат СМЭВ. 27 | * @param certificate сертификат, предположительно принадлежащий СМЭВ. 28 | * @return true, если такой сертификат известен как принадлежащий СМЭВ. 29 | * @throws IOException невозможно прочитать сертификаты из долговременной памяти. 30 | * @throws CertificateException невозможно преобразовать сертификат из бинарного формата в Java-объект. 31 | */ 32 | public abstract boolean isKnown(X509Certificate certificate) throws IOException, CertificateException; 33 | } 34 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/CacheOptions.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | public final class CacheOptions { 4 | 5 | private Long expireAfterAccess = null; 6 | private Long expireAfterWrite = null; 7 | private Long maxSize = null; 8 | private boolean cachePrivateKeys = false; 9 | private boolean cacheCertificates = false; 10 | 11 | public CacheOptions expireAfterAccess(long ms) { 12 | expireAfterAccess = ms; 13 | return this; 14 | } 15 | 16 | public CacheOptions expireAfterWrite(long ms) { 17 | expireAfterWrite = ms; 18 | return this; 19 | } 20 | 21 | public CacheOptions maxSize(long size) { 22 | maxSize = size; 23 | return this; 24 | } 25 | 26 | public CacheOptions cachePrivateKeys() { 27 | cachePrivateKeys = true; 28 | return this; 29 | } 30 | 31 | public CacheOptions cacheCertificates() { 32 | cacheCertificates = true; 33 | return this; 34 | } 35 | 36 | public Long getExpireAfterAccess() { 37 | return expireAfterAccess; 38 | } 39 | 40 | public Long getExpireAfterWrite() { 41 | return expireAfterWrite; 42 | } 43 | 44 | public Long getMaxSize() { 45 | return maxSize; 46 | } 47 | 48 | public boolean isCachePrivateKeys() { 49 | return cachePrivateKeys; 50 | } 51 | 52 | public boolean isCacheCertificates() { 53 | return cacheCertificates; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/intercept/InterceptorStorage.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.intercept; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | /** 6 | * @author bshalin@it.ru 7 | */ 8 | public final class InterceptorStorage { 9 | 10 | private static final InterceptorStorage request = new InterceptorStorage(); 11 | private static final InterceptorStorage response = new InterceptorStorage(); 12 | 13 | private final ThreadLocal data = new ThreadLocal(); 14 | private final ThreadLocal intercept = new ThreadLocal(); 15 | 16 | public static InterceptorStorage getRequest() { 17 | return request; 18 | } 19 | 20 | public static InterceptorStorage getResponse() { 21 | return response; 22 | } 23 | 24 | public void set(byte[] data) { 25 | this.data.set(data); 26 | } 27 | 28 | public byte[] get() { 29 | return data.get(); 30 | } 31 | 32 | public String getString() { 33 | byte[] bytes = get(); 34 | if (bytes == null) 35 | return null; 36 | try { 37 | return new String(bytes, "UTF-8"); 38 | } catch (UnsupportedEncodingException ex) { 39 | return new String(bytes); 40 | } 41 | } 42 | 43 | public void setIntercept(boolean on) { 44 | intercept.set(on); 45 | } 46 | 47 | public boolean isIntercept() { 48 | Boolean value = intercept.get(); 49 | return value != null && value.booleanValue(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/FileSystemSMEVCertificateStore.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import java.io.*; 4 | import java.security.cert.CertificateException; 5 | import java.security.cert.CertificateFactory; 6 | import java.security.cert.X509Certificate; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Хранилище сертификатов СМЭВ на базе файловой системы. 12 | * @author dpryakhin 13 | */ 14 | public class FileSystemSMEVCertificateStore extends AbstractSMEVCertificateStore { 15 | 16 | private final File directory; 17 | 18 | public FileSystemSMEVCertificateStore(File directory) { 19 | this.directory = directory; 20 | } 21 | 22 | @Override 23 | protected List getSMEVCertificates() throws IOException, CertificateException { 24 | List result = new ArrayList<>(); 25 | 26 | File[] files = directory.listFiles(); 27 | if (files != null) { 28 | for (File item : files) { 29 | if (item.isDirectory()) { 30 | continue; 31 | } 32 | try (InputStream in = new FileInputStream(item)) { 33 | BufferedInputStream b64in = new BufferedInputStream(in); 34 | CertificateFactory cf = CertificateFactory.getInstance("X.509"); 35 | X509Certificate cert = (X509Certificate) cf.generateCertificate(b64in); 36 | result.add(cert); 37 | } 38 | } 39 | } 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/util/PipeOutputStream.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.util; 2 | 3 | import ru.voskhod.smev.message_exchange_service_client.SignatureOperationsClient; 4 | import ru.voskhod.crypto.DigitalSignatureFactory; 5 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 6 | import ru.voskhod.crypto.exceptions.SignatureValidationException; 7 | 8 | import java.io.FilterOutputStream; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.security.MessageDigest; 12 | import java.security.cert.X509Certificate; 13 | 14 | public class PipeOutputStream extends FilterOutputStream { 15 | 16 | private final MessageDigest digest; 17 | 18 | public PipeOutputStream(OutputStream out) throws SignatureProcessingException { 19 | this(out, SignatureOperationsClient.getMessageDigest()); 20 | } 21 | 22 | public PipeOutputStream(OutputStream out, MessageDigest digest) { 23 | super(out); 24 | this.digest = digest; 25 | } 26 | 27 | @Override 28 | public void write(int b) throws IOException { 29 | out.write(b); 30 | digest.update((byte) b); 31 | } 32 | 33 | @Override 34 | public void write(byte[] b, int off, int len) throws IOException { 35 | out.write(b, off, len); 36 | digest.update(b, off, len); 37 | } 38 | 39 | public byte[] getDigest() { 40 | return digest.digest(); 41 | } 42 | 43 | public X509Certificate validatePKCS7Signature(byte[] signature) throws SignatureProcessingException, SignatureValidationException { 44 | return DigitalSignatureFactory.getDigitalSignatureProcessor().validatePKCS7Signature(getDigest(), signature); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/JCPCMSTools.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | public class JCPCMSTools { 4 | 5 | /** 6 | * OIDs для CMS 7 | */ 8 | public static final String OID_PKCS7 = "1.2.840.113549.1.7"; 9 | public static final String OID_PKCS9 = "1.2.840.113549.1.9"; 10 | 11 | // ====== PKCS#7 content types ======= 12 | // data 13 | public static final String STR_CMS_OID_DATA = OID_PKCS7 + ".1"; 14 | // signedData 15 | public static final String STR_CMS_OID_SIGNED = OID_PKCS7 + ".2"; 16 | // envelopedData 17 | public static final String STR_CMS_OID_ENVELOPED = OID_PKCS7 + ".3"; 18 | // signedAndEnvelopedData 19 | public static final String STR_CMS_OID_SIGNED_AND_ENVELOPED = OID_PKCS7 + ".4"; 20 | // digestedData 21 | public static final String STR_CMS_OID_DIGESTED = OID_PKCS7 + ".5"; 22 | // encryptedData 23 | public static final String STR_CMS_OID_ENCRYPTED = OID_PKCS7 + ".6"; 24 | 25 | // ====== PKCS#9 OIDs ======= 26 | // contentType 27 | public static final String STR_CMS_OID_CONT_TYP_ATTR = OID_PKCS9 + ".3"; 28 | // messageDigest 29 | public static final String STR_CMS_OID_DIGEST_ATTR = OID_PKCS9 + ".4"; 30 | // Signing Time 31 | public static final String STR_CMS_OID_SIGN_TYM_ATTR = OID_PKCS9 + ".5"; 32 | // Time-stamp token 33 | public static final String STR_CMS_OID_TS = OID_PKCS9 + ".4"; 34 | 35 | // ====== OIDs алгоритмов ГОСТ ====== 36 | // GOST R 34.11-94 - Russian hash algorithm. 37 | public static final String DIGEST_OID = "1.2.643.2.2.9"; 38 | // GOST R 34.10-2001 - Russian encryption algorithm 39 | public static final String SIGN_OID = "1.2.643.2.2.19"; 40 | 41 | /** 42 | * GOST digest and sig identifiers 43 | */ 44 | public static final String GOST_EL_SIGN_NAME = "GOST3411withGOST3410EL"; 45 | public static final String GOST_DIGEST_NAME = "GOST3411"; 46 | 47 | } -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/SmallOutAttachment.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.AttachmentContentType; 4 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.AttachmentHeaderType; 5 | import ru.voskhod.smev.message_exchange_service_client.OutAttachment; 6 | import ru.voskhod.smev.message_exchange_service_client.SomeMimeTypes; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | 12 | public final class SmallOutAttachment implements OutAttachment { 13 | 14 | private final AttachmentHeaderType header; 15 | private final AttachmentContentType attachment; 16 | 17 | public SmallOutAttachment(AttachmentHeaderType header, AttachmentContentType attachment) { 18 | this.header = header; 19 | this.attachment = attachment; 20 | } 21 | 22 | public String getName() { 23 | return attachment.getId() + SomeMimeTypes.guessFileExtension(getMimeType()); 24 | } 25 | 26 | public String getMimeType() { 27 | return header == null ? null : header.getMimeType(); 28 | } 29 | 30 | public byte[] getSignature() { 31 | return header == null ? null : header.getSignaturePKCS7(); 32 | } 33 | 34 | public Content getContent() { 35 | return new SmallContent(); 36 | } 37 | 38 | private final class SmallContent implements Content { 39 | 40 | private volatile boolean cancelled = false; 41 | 42 | public boolean retrieve(OutputStream output) throws IOException { 43 | try (InputStream is = attachment.getContent().getInputStream()) { 44 | byte[] buf = new byte[8192]; 45 | while (!cancelled) { 46 | int read = is.read(buf); 47 | if (read < 0) 48 | return true; 49 | output.write(buf, 0, read); 50 | } 51 | return false; 52 | } 53 | } 54 | 55 | public void cancel() { 56 | cancelled = true; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/csp_tj/TrustedKeyStoreWrapperCSP.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl.csp_tj; 2 | 3 | import ru.voskhod.crypto.KeyStoreWrapper; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.io.InputStream; 7 | import java.security.*; 8 | import java.security.cert.CertificateException; 9 | import java.security.cert.CertificateFactory; 10 | import java.security.cert.X509Certificate; 11 | 12 | public class TrustedKeyStoreWrapperCSP implements KeyStoreWrapper { 13 | 14 | public static final String CURRENT_USER_KS_TYPE = "CurrentUser"; 15 | public static final String LOCAL_COMPUTER_KS_TYPE = "LocalComputer"; 16 | public static final String CURRENT_USER_CONTAINERS_KS_TYPE = "CurrentUser/Containers"; 17 | 18 | private final KeyStore ks; 19 | 20 | /** 21 | * Загружает хранилище ключей указанного типа. 22 | */ 23 | public TrustedKeyStoreWrapperCSP() throws Exception { 24 | ks = KeyStore.getInstance("CryptoProCSPKeyStore"); 25 | InputStream stream = new ByteArrayInputStream(CURRENT_USER_CONTAINERS_KS_TYPE.getBytes("UTF-8")); 26 | ks.load(stream, null); 27 | } 28 | 29 | public PrivateKey getPrivateKey(String alias, char[] password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { 30 | alias = resolveAlias(alias); 31 | return (PrivateKey) ks.getKey(alias, password); 32 | } 33 | 34 | public X509Certificate getX509Certificate(String alias) throws CertificateException, KeyStoreException { 35 | alias = resolveAlias(alias); 36 | X509Certificate certificate = (X509Certificate) ks.getCertificate(alias); 37 | if (certificate == null) 38 | return null; 39 | return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(certificate.getEncoded())); 40 | } 41 | 42 | public KeyStore getKeyStore() { 43 | return ks; 44 | } 45 | 46 | //TODO Loskutov Нужен кэш, нужен поиск по алиасам для CSP под Linux. 47 | private String resolveAlias(String alias) { 48 | // TODO Loskutov, На линухе не пашет, ибо алимасы начинаются с HDIMAGE\\. 49 | return alias; 50 | /*if (!alias.contains("REGISTRY\\\\")) { 51 | return "REGISTRY\\\\" + alias; 52 | } 53 | return alias;*/ 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/intercept/InterceptorTube.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.intercept; 2 | 3 | import com.sun.xml.ws.api.message.Packet; 4 | import com.sun.xml.ws.api.pipe.NextAction; 5 | import com.sun.xml.ws.api.pipe.Tube; 6 | import com.sun.xml.ws.api.pipe.TubeCloner; 7 | import com.sun.xml.ws.api.pipe.helper.AbstractFilterTubeImpl; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.IOException; 12 | 13 | final class InterceptorTube extends AbstractFilterTubeImpl { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(InterceptorTube.class); 16 | 17 | private final boolean client; 18 | 19 | InterceptorTube(Tube tube, boolean client) { 20 | super(tube); 21 | this.client = client; 22 | } 23 | 24 | private InterceptorTube(InterceptorTube original, TubeCloner cloner) { 25 | super(original, cloner); 26 | this.client = original.client; 27 | } 28 | 29 | @Override 30 | public InterceptorTube copy(TubeCloner cloner) { 31 | return new InterceptorTube(this, cloner); 32 | } 33 | 34 | private void intercept(String what, InterceptorStorage storage, Packet packet) { 35 | if (storage.isIntercept()) { 36 | logger.debug(String.format("Message %s intercepted on %s side", what, client ? "client" : "server")); 37 | try { 38 | CustomByteArrayOutputStream baos = new CustomByteArrayOutputStream(); 39 | Packet requestCopy = packet.copy(true); 40 | requestCopy.writeTo(baos); 41 | storage.set(baos.getParsedContent()); 42 | } catch (IOException ex) { 43 | logger.error(null, ex); 44 | } 45 | } 46 | } 47 | 48 | @Override 49 | public NextAction processRequest(Packet request) { 50 | intercept("request", InterceptorStorage.getRequest(), request); 51 | return super.processRequest(request); 52 | } 53 | 54 | @Override 55 | public NextAction processResponse(Packet response) { 56 | intercept("response", InterceptorStorage.getResponse(), response); 57 | return super.processResponse(response); 58 | } 59 | 60 | @Override 61 | public NextAction processException(Throwable throwable) { 62 | logger.warn(String.format("Message processing exception intercepted on %s side", client ? "client" : "server")); 63 | return super.processException(throwable); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/PipeInputStream.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.security.MessageDigest; 6 | 7 | public class PipeInputStream extends InputStream { 8 | 9 | private static final int MAX_UPDATE = 65535; 10 | 11 | private final InputStream wrapped; 12 | private final MessageDigest digest; 13 | 14 | // for non-streamed providers 15 | private final byte[] digestResult; 16 | 17 | private boolean exhausted = false; 18 | private long size = 0; 19 | 20 | public PipeInputStream(InputStream arg, MessageDigest argDigest) { 21 | this.wrapped = arg; 22 | this.digest = argDigest; 23 | this.digestResult = null; 24 | } 25 | 26 | public PipeInputStream(byte[] digestResult) { 27 | this.wrapped = null; 28 | this.digest = null; 29 | this.digestResult = digestResult; 30 | } 31 | 32 | public byte[] getDigest() { 33 | return digest != null ? digest.digest() : digestResult; 34 | } 35 | 36 | public long getSize() { 37 | return size; 38 | } 39 | 40 | @Override 41 | public void close() throws IOException { 42 | if (wrapped != null) { 43 | wrapped.close(); 44 | } 45 | } 46 | 47 | @Override 48 | public boolean markSupported() { 49 | return false; 50 | } 51 | 52 | @Override 53 | public int read() throws IOException { 54 | if (wrapped == null || exhausted) 55 | return -1; 56 | 57 | int value = wrapped.read(); 58 | if (value < 0) { 59 | exhausted = true; 60 | } else { 61 | size++; 62 | digest.update((byte) value); 63 | } 64 | return value; 65 | } 66 | 67 | @Override 68 | public int read(byte[] b, int off, int len) throws IOException { 69 | if (wrapped == null || exhausted) 70 | return -1; 71 | 72 | int read = wrapped.read(b, off, len); 73 | if (read < 0) { 74 | exhausted = true; 75 | } else { 76 | size += read; 77 | int updOff = off; 78 | int updLen = read; 79 | while (updLen > MAX_UPDATE) { 80 | digest.update(b, updOff, MAX_UPDATE); 81 | updOff += MAX_UPDATE; 82 | updLen -= MAX_UPDATE; 83 | } 84 | digest.update(b, updOff, updLen); 85 | return read; 86 | } 87 | return read; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/InAttachment.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import ru.voskhod.crypto.PipeInputStream; 4 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | public abstract class InAttachment { 10 | 11 | private final String mimeType; 12 | /** 13 | * Длина файла вложения. Может быть null, но при этом используется больше ресурсов при useFS = AUTO. 14 | */ 15 | private final Long length; 16 | private String id = null; 17 | private byte[] personalSignature = null; 18 | 19 | /** 20 | * @param length размер файла вложения. 21 | * Если null, то используется больше ресурсов при отправке файла. 22 | */ 23 | protected InAttachment(String mimeType, Long length) { 24 | this.mimeType = mimeType; 25 | this.length = length; 26 | } 27 | 28 | protected InAttachment(String mimeType) { 29 | this(mimeType, null); 30 | } 31 | 32 | public String getMimeType() { 33 | return mimeType; 34 | } 35 | 36 | public Long getLength() { 37 | return length; 38 | } 39 | 40 | public DigestResult getDigest() throws IOException, SignatureProcessingException { 41 | try (InputStream inputStream = getInputStream()) { 42 | PipeInputStream digestStream = SignatureOperationsClient.getDigestCollectingInputStream(inputStream); 43 | while (true) { 44 | int c = digestStream.read(); 45 | if (c < 0) 46 | break; 47 | } 48 | return new DigestResult(digestStream); 49 | } 50 | } 51 | 52 | public void setSignPersonal(SignatureOperationsClient signer) throws SignatureProcessingException, IOException { 53 | if (signer == null) { 54 | setPersonalSignature(null); 55 | } else { 56 | byte[] dataDigest = getDigest().getDataDigest(); 57 | setPersonalSignature(signer.signPKCS7Detached(dataDigest)); 58 | } 59 | } 60 | 61 | public byte[] getPersonalSignature() { 62 | return personalSignature; 63 | } 64 | 65 | public void setPersonalSignature(byte[] personalSignature) { 66 | this.personalSignature = personalSignature; 67 | } 68 | 69 | public String getId() { 70 | return id; 71 | } 72 | 73 | public void setId(String id) { 74 | this.id = id; 75 | } 76 | 77 | /** 78 | * Этот метод может вызываться несколько раз и каждый раз должен возвращать новый поток 79 | */ 80 | public abstract InputStream getInputStream() throws IOException; 81 | } 82 | -------------------------------------------------------------------------------- /client_1.11/crypto/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 4.0.0 6 | 7 | ru.voskhod.crypto 8 | crypto 9 | 1.0-SNAPSHOT 10 | jar 11 | crypto 12 | 13 | 14 | 15 | UTF-8 16 | true 17 | 18 | 19 | 20 | 21 | org.bouncycastle 22 | bcprov-jdk15on 23 | 1.47 24 | 25 | 26 | org.bouncycastle 27 | bcpkix-jdk15on 28 | 1.47 29 | 30 | 31 | com.google.guava 32 | guava 33 | 17.0 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 3.1 43 | 44 | 1.7 45 | 1.7 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-source-plugin 52 | 2.2.1 53 | 54 | 55 | package 56 | 57 | jar-no-fork 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | releases 68 | releases 69 | http://nexus/nexus-2.7.0/content/repositories/releases/ 70 | 71 | 72 | snapshots 73 | snapshots 74 | http://nexus/nexus-2.7.0/content/repositories/snapshots/ 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/AbstractSMEVCertificateStore.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import ru.voskhod.smev.message_exchange_service_client.SMEVCertificateStore; 4 | 5 | import java.io.IOException; 6 | import java.security.cert.CertificateException; 7 | import java.security.cert.X509Certificate; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Абстрактный класс - хранилище, в котором лежат сертификаты СМЭВ. 13 | * Эти сертификаты используются, чтобы убеждаться, что ЭП-СМЭВ проставлена действительно СМЭВ. 14 | * Наследники реализуют выборку сертификатов, например, из файловой системы, БД, или KeyStore. 15 | * @author dpryakhin 16 | */ 17 | public abstract class AbstractSMEVCertificateStore extends SMEVCertificateStore { 18 | 19 | private static final long CACHE_UPDATE_INTERVAL_MILLIS = 1000; 20 | 21 | private final List certificateCache = new ArrayList<>(); 22 | private final List negativeCertificateCache = new ArrayList<>(); 23 | private long lastCacheUpdate = 0; 24 | 25 | /** 26 | * Известен ли такой сертификат СМЭВ. 27 | * @param certificate сертификат, предположительно принадлежащий СМЭВ. 28 | * @return true, если такой сертификат известен как принадлежащий СМЭВ. 29 | * @throws java.io.IOException невозможно прочитать сертификаты из долговременной памяти. 30 | * @throws java.security.cert.CertificateException невозможно преобразовать сертификат из бинарного формата в Java-объект. 31 | */ 32 | public boolean isKnown(X509Certificate certificate) throws IOException, CertificateException { 33 | // Может случиться так, что по ходу работы приложения в хранилище добавили сертификат. 34 | // Поэтому при отсутствии его в кэше пытаемся обновить кэш. 35 | synchronized (this) { 36 | if (certificateCache.contains(certificate)) 37 | return true; 38 | long now = System.currentTimeMillis(); 39 | if (negativeCertificateCache.contains(certificate) && now - lastCacheUpdate <= CACHE_UPDATE_INTERVAL_MILLIS) 40 | return false; 41 | 42 | certificateCache.clear(); 43 | negativeCertificateCache.clear(); 44 | certificateCache.addAll(getSMEVCertificates()); 45 | lastCacheUpdate = System.currentTimeMillis(); 46 | 47 | boolean found = certificateCache.contains(certificate); 48 | if (!found) { 49 | negativeCertificateCache.add(certificate); 50 | } 51 | return found; 52 | } 53 | } 54 | 55 | /** 56 | * Прочитать сертификаты СМЭВ из долговременной памяти. 57 | * @return список сертификатов. 58 | * @throws java.io.IOException невозможно прочитать сертификаты из долговременной памяти. 59 | * @throws java.security.cert.CertificateException невозможно преобразовать сертификат из бинарного формата в Java-объект. 60 | */ 61 | protected abstract List getSMEVCertificates() throws IOException, CertificateException; 62 | } 63 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/SignatureOperationsClient.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import org.w3c.dom.Element; 4 | import ru.voskhod.crypto.DigitalSignatureFactory; 5 | import ru.voskhod.crypto.PipeInputStream; 6 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 7 | import ru.voskhod.crypto.exceptions.SignatureValidationException; 8 | 9 | import java.io.InputStream; 10 | import java.security.MessageDigest; 11 | import java.security.NoSuchAlgorithmException; 12 | import java.security.PrivateKey; 13 | import java.security.cert.X509Certificate; 14 | 15 | public final class SignatureOperationsClient { 16 | 17 | private final PrivateKey privateKey; 18 | private final X509Certificate certificate; 19 | 20 | public SignatureOperationsClient(PrivateKey privateKey, X509Certificate certificate) { 21 | if (privateKey == null) 22 | throw new IllegalArgumentException("Private key cannot be null"); 23 | if (certificate == null) 24 | throw new IllegalArgumentException("Certificate cannot be null"); 25 | this.privateKey = privateKey; 26 | this.certificate = certificate; 27 | } 28 | 29 | public Element signXMLDSigDetached(Element document2Sign, String signatureId) throws SignatureProcessingException { 30 | return DigitalSignatureFactory.getDigitalSignatureProcessor().signXMLDSigDetached(document2Sign, signatureId, privateKey, certificate); 31 | } 32 | 33 | public X509Certificate validateXMLDSigSignature(Element signedContent, Element detachedSignature) 34 | throws SignatureProcessingException, SignatureValidationException { 35 | return DigitalSignatureFactory.getDigitalSignatureProcessor().validateXMLDSigDetachedSignature(signedContent, detachedSignature); 36 | } 37 | 38 | public static MessageDigest getMessageDigest() throws SignatureProcessingException { 39 | MessageDigest digest; 40 | try { 41 | digest = MessageDigest.getInstance("GOST3411"); 42 | } catch (NoSuchAlgorithmException e) { 43 | throw new SignatureProcessingException("Криптопровайдер не поддерживает алгоритм ГОСТ Р 34.11-94", e); 44 | } 45 | return digest; 46 | } 47 | 48 | public static PipeInputStream getDigestCollectingInputStream(InputStream signedContent) throws SignatureProcessingException { 49 | MessageDigest digest = getMessageDigest(); 50 | return new PipeInputStream(signedContent, digest); 51 | } 52 | 53 | public byte[] signPKCS7Detached(byte[] digest) throws SignatureProcessingException { 54 | return DigitalSignatureFactory.getDigitalSignatureProcessor().signPKCS7Detached(digest, privateKey, certificate); 55 | } 56 | 57 | public X509Certificate validatePKCS7Signature(byte[] digest, byte[] signature) 58 | throws SignatureProcessingException, SignatureValidationException { 59 | return DigitalSignatureFactory.getDigitalSignatureProcessor().validatePKCS7Signature(digest, signature); 60 | } 61 | 62 | public X509Certificate getCertificate() { 63 | return certificate; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/LargeOutAttachment.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import it.sauronsoftware.ftp4j.*; 4 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.FSAuthInfo; 5 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.RefAttachmentHeaderType; 6 | import ru.voskhod.smev.message_exchange_service_client.ClientSideProcessingException; 7 | import ru.voskhod.smev.message_exchange_service_client.OutAttachment; 8 | 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | 12 | public final class LargeOutAttachment implements OutAttachment { 13 | 14 | private final String ftpAddress; 15 | private final RefAttachmentHeaderType header; 16 | private final FSAuthInfo info; 17 | 18 | public LargeOutAttachment(String ftpAddress, RefAttachmentHeaderType header, FSAuthInfo info) { 19 | this.ftpAddress = ftpAddress; 20 | this.header = header; 21 | this.info = info; 22 | } 23 | 24 | public String getName() { 25 | return info.getFileName(); 26 | } 27 | 28 | public String getMimeType() { 29 | return header == null ? null : header.getMimeType(); 30 | } 31 | 32 | public byte[] getSignature() { 33 | return header == null ? null : header.getSignaturePKCS7(); 34 | } 35 | 36 | public Content getContent() { 37 | return new LargeContent(); 38 | } 39 | 40 | private final class LargeContent implements Content { 41 | 42 | private final FTPClient ftp = new FTPClient(); 43 | 44 | public boolean retrieve(OutputStream output) throws IOException, ClientSideProcessingException { 45 | try { 46 | AttachmentBuilder.connect(ftp, ftpAddress); 47 | boolean ok = false; 48 | try { 49 | ftp.login(info.getUserName(), info.getPassword()); 50 | ftp.download(info.getFileName(), output, 0, null); 51 | ok = true; 52 | return true; 53 | } finally { 54 | ftp.disconnect(ok); 55 | } 56 | } catch (FTPAbortedException ex) { 57 | return false; 58 | } catch (FTPIllegalReplyException ex) { 59 | throw new ClientSideProcessingException(ex); 60 | } catch (FTPException ex) { 61 | throw new ClientSideProcessingException(ex); 62 | } catch (FTPDataTransferException ex) { 63 | throw new ClientSideProcessingException(ex); 64 | } 65 | } 66 | 67 | public void cancel() { 68 | if (ftp.isConnected()) { 69 | try { 70 | ftp.abortCurrentDataTransfer(true); 71 | } catch (Exception ex) { 72 | try { 73 | ftp.abortCurrentDataTransfer(false); 74 | } catch (Exception ex2) { 75 | // ignore 76 | } 77 | } 78 | } else { 79 | ftp.abortCurrentConnectionAttempt(); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/resources/META-INF/metro.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /client_1.11/client/xml-artifacts/smev-iemk-eln-service.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Электронный лист нетрудоспособности 9 | 10 | 11 | 12 | 13 | Запрос на листы нетрудоспособности 14 | 15 | 16 | 17 | 18 | 19 | Имя 20 | 21 | 22 | 23 | 24 | Фамилия 25 | 26 | 27 | 28 | 29 | Отчество 30 | 31 | 32 | 33 | 34 | Снилс 35 | 36 | 37 | 38 | 39 | Дата начиная с которой нужны документы 40 | 41 | 42 | 43 | 44 | Дата по которую нужны документы 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Ответ на запрос листов нетрудоспособности 54 | 55 | 56 | 57 | 58 | 59 | Снилс 60 | 61 | 62 | 63 | 64 | Количество документов 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/intercept/CustomByteArrayOutputStream.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.intercept; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | final class CustomByteArrayOutputStream extends ByteArrayOutputStream { 6 | 7 | private static final String UUID_START = "--uuid:"; 8 | private static final String UUID_PATTERN = "12345678-1234-1234-1234-1234567890AB"; 9 | private static final int MAX_LENGTH = UUID_START.length() + UUID_PATTERN.length(); 10 | 11 | private int start1 = -1; 12 | private int start2 = -1; 13 | 14 | @Override 15 | public synchronized void write(byte[] b, int off, int len) { 16 | for (int i = off; i < len - off; i++) { 17 | write(b[i]); 18 | } 19 | } 20 | 21 | @Override 22 | public synchronized void write(int b) { 23 | if (start2 >= 0) 24 | return; 25 | super.write(b); 26 | if (isPartSeparator()) { 27 | if (start1 >= 0) { 28 | // это уже второй разделитель 29 | start2 = count - MAX_LENGTH; 30 | } else { 31 | start1 = count; 32 | } 33 | } 34 | } 35 | 36 | private static boolean isAlphaNumeric(int c) { 37 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 38 | } 39 | 40 | private boolean isPartSeparator() { 41 | if (count < MAX_LENGTH) 42 | return false; 43 | int start = count - MAX_LENGTH; 44 | for (int i = start, j = 0; j < UUID_START.length(); i++, j++) { 45 | if (buf[i] != UUID_START.charAt(j)) 46 | return false; 47 | } 48 | for (int i = start + UUID_START.length(), j = 0; j < UUID_PATTERN.length(); i++, j++) { 49 | int b = buf[i]; 50 | char c = UUID_PATTERN.charAt(j); 51 | if (c == '-') { 52 | if (b != '-') 53 | return false; 54 | } else { 55 | if (!isAlphaNumeric(b)) 56 | return false; 57 | } 58 | } 59 | return true; 60 | } 61 | 62 | private int findBodyStart(int from, int to) { 63 | int i = from; 64 | int prevN = 0; 65 | int prevR = 0; 66 | while (i < to) { 67 | int b = buf[i++]; 68 | if (b == '\n' || b == '\r') { 69 | if (b == '\n') { 70 | prevN++; 71 | } else { 72 | prevR++; 73 | } 74 | if (prevN >= 2) { 75 | if (prevR >= 2 || prevR == 0) 76 | return i; 77 | } else if (prevR >= 2) { 78 | if (prevN >= 2 || prevN == 0) 79 | return i; 80 | } 81 | } else { 82 | prevN = prevR = 0; 83 | } 84 | } 85 | return from; 86 | } 87 | 88 | public synchronized byte[] getParsedContent() { 89 | if (start1 >= 0 && start2 >= 0) { 90 | int from = findBodyStart(start1, start2); 91 | byte[] parsed = new byte[start2 - from]; 92 | System.arraycopy(buf, from, parsed, 0, parsed.length); 93 | return parsed; 94 | } else { 95 | return toByteArray(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /client_1.11/client/src/test/resources/smev-iemk-eln-service.xsd: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | Электронный лист нетрудоспособности 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Запрос на листы нетрудоспособности 27 | 28 | 29 | 30 | 31 | 32 | Имя 33 | 34 | 35 | 36 | 37 | Фамилия 38 | 39 | 40 | 41 | 42 | Отчество 43 | 44 | 45 | 46 | 47 | Снилс 48 | 49 | 50 | 51 | 52 | Дата начиная с которой нужны документы 53 | 54 | 55 | 56 | 57 | Дата по которую нужны документы 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Ответ на запрос листов нетрудоспособности 67 | 68 | 69 | 70 | 71 | 72 | Снилс 73 | 74 | 75 | 76 | 77 | Количество документов 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/BusinessProcessMetadataBuilder.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.Element; 5 | import ru.voskhod.smev.message_exchange.autogenerated.bp_metadata.v1_0.CaseInfoType; 6 | import ru.voskhod.smev.message_exchange.autogenerated.bp_metadata.v1_0.ObjectFactory; 7 | import ru.voskhod.smev.message_exchange.autogenerated.bp_metadata.v1_0.ServiceOrFunctionType; 8 | 9 | import javax.xml.bind.JAXBContext; 10 | import javax.xml.bind.JAXBElement; 11 | import javax.xml.bind.JAXBException; 12 | import javax.xml.bind.Marshaller; 13 | import javax.xml.transform.dom.DOMResult; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public final class BusinessProcessMetadataBuilder { 18 | 19 | private static final ThreadLocal jaxbMarshaller = new ThreadLocal() { 20 | @Override 21 | protected Marshaller initialValue() { 22 | try { 23 | return JAXBContext.newInstance("ru.voskhod.smev.message_exchange.autogenerated.bp_metadata.v1_0").createMarshaller(); 24 | } catch (JAXBException e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | }; 29 | 30 | private String caseNumber; 31 | private String serviceOrFunctionCode; 32 | private boolean service; 33 | private String frgu; 34 | 35 | public BusinessProcessMetadataBuilder() { 36 | reset(); 37 | } 38 | 39 | /** 40 | * Установить номер канцелярского дела, в ходе обработки которого посылается запрос или заявка. 41 | * 42 | * @param caseNumber номер дела. 43 | */ 44 | public void setCaseNumber(String caseNumber) { 45 | this.caseNumber = caseNumber; 46 | } 47 | 48 | /** 49 | * Установить код госуслуги или госфункции, в ходе предоставления (выполнения) которой посылается запрос или заявка. 50 | * Согласно реестру госуслуг. 51 | * 52 | * @param serviceOrFunctionCode код госуслуги или госфункции. 53 | */ 54 | public void setServiceOrFunctionCode(String serviceOrFunctionCode) { 55 | this.serviceOrFunctionCode = serviceOrFunctionCode; 56 | } 57 | 58 | /** 59 | * Установить признак - запрос отаравляется в ходе предоставления госуслуги, или выполнения госфункции. 60 | * 61 | * @param service true - госуслуга, false - госфункция. 62 | */ 63 | public void setService(boolean service) { 64 | this.service = service; 65 | } 66 | 67 | /** 68 | * Установить мнемонику участника взаимодействия ФРГУ 69 | * 70 | * @param frgu мнемоника участника взаимодействия ФРГУ 71 | */ 72 | public void setFrgu(String frgu) { 73 | this.frgu = frgu; 74 | } 75 | 76 | public String getCaseNumber() { 77 | return caseNumber; 78 | } 79 | 80 | public String getServiceOrFunctionCode() { 81 | return serviceOrFunctionCode; 82 | } 83 | 84 | public boolean isService() { 85 | return service; 86 | } 87 | 88 | public String getFrgu() { 89 | return frgu; 90 | } 91 | 92 | private static Element unmarshal(JAXBElement element) throws JAXBException { 93 | DOMResult domResult = new DOMResult(); 94 | jaxbMarshaller.get().marshal(element, domResult); 95 | return ((Document) domResult.getNode()).getDocumentElement(); 96 | } 97 | 98 | /** 99 | * Получить результат в виде DOM-фрагмента. 100 | * 101 | * @return 102 | * @throws JAXBException 103 | */ 104 | public List getResult() throws JAXBException { 105 | List result = new ArrayList<>(); 106 | ObjectFactory factory = new ObjectFactory(); 107 | if (caseNumber != null || serviceOrFunctionCode != null) { 108 | CaseInfoType caseInfo = new CaseInfoType(); 109 | caseInfo.setCaseNumber(caseNumber); 110 | if (serviceOrFunctionCode != null) { 111 | caseInfo.setServiceOrFunctionCode(serviceOrFunctionCode); 112 | if (service) { 113 | caseInfo.setServiceOrFunction(ServiceOrFunctionType.SERVICE); 114 | } else { 115 | caseInfo.setServiceOrFunction(ServiceOrFunctionType.FUNCTION); 116 | } 117 | } 118 | result.add(unmarshal(factory.createCaseInfo(caseInfo))); 119 | } 120 | if (frgu != null) { 121 | result.add(unmarshal(factory.createFrgu(frgu))); 122 | } 123 | if (result.isEmpty()) 124 | return null; 125 | return result; 126 | } 127 | 128 | /** 129 | * Подготовить builder к повторному использованию. 130 | */ 131 | public void reset() { 132 | caseNumber = null; 133 | serviceOrFunctionCode = null; 134 | service = true; 135 | frgu = null; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/CachingKeyStoreWrapper.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.cache.Cache; 5 | import com.google.common.cache.CacheBuilder; 6 | import com.google.common.cache.CacheLoader; 7 | import com.google.common.cache.LoadingCache; 8 | import ru.voskhod.crypto.KeyStoreWrapper; 9 | 10 | import java.nio.CharBuffer; 11 | import java.security.*; 12 | import java.security.cert.CertificateException; 13 | import java.security.cert.X509Certificate; 14 | import java.util.Arrays; 15 | import java.util.concurrent.Callable; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | public final class CachingKeyStoreWrapper implements KeyStoreWrapper { 20 | 21 | private final KeyStoreWrapper original; 22 | 23 | private static final class PKEntry { 24 | 25 | private final PrivateKey pk; 26 | private final byte[] passHash; 27 | 28 | private PKEntry(PrivateKey pk, byte[] passHash) { 29 | this.pk = pk; 30 | this.passHash = passHash; 31 | } 32 | } 33 | 34 | private final Cache keyCache; 35 | private final LoadingCache certCache; 36 | 37 | public CachingKeyStoreWrapper(KeyStoreWrapper keyStore, CacheOptions options) { 38 | this.original = keyStore; 39 | CacheBuilder builder = CacheBuilder.newBuilder(); 40 | if (options.getExpireAfterAccess() != null) { 41 | builder.expireAfterAccess(options.getExpireAfterAccess().longValue(), TimeUnit.MILLISECONDS); 42 | } 43 | if (options.getExpireAfterWrite() != null) { 44 | builder.expireAfterWrite(options.getExpireAfterWrite().longValue(), TimeUnit.MILLISECONDS); 45 | } 46 | if (options.getMaxSize() != null) { 47 | builder.maximumSize(options.getMaxSize().longValue()); 48 | } 49 | if (options.isCachePrivateKeys()) { 50 | keyCache = builder.build(); 51 | } else { 52 | keyCache = null; 53 | } 54 | if (options.isCacheCertificates()) { 55 | certCache = builder.build(new CacheLoader() { 56 | public X509Certificate load(String key) throws Exception { 57 | return original.getX509Certificate(key); 58 | } 59 | }); 60 | } else { 61 | certCache = null; 62 | } 63 | } 64 | 65 | @SuppressWarnings("unchecked") 66 | private static void checkException(Throwable ex, Class cls) throws T { 67 | if (cls.isInstance(ex)) { 68 | throw (T) ex; 69 | } 70 | } 71 | 72 | private static byte[] hash(char[] password) throws NoSuchAlgorithmException { 73 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 74 | byte[] data = Charsets.UTF_16.encode(CharBuffer.wrap(password)).array(); 75 | md.update(data); 76 | return md.digest(); 77 | } 78 | 79 | @Override 80 | public PrivateKey getPrivateKey(final String alias, final char[] password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { 81 | if (keyCache == null) { 82 | return original.getPrivateKey(alias, password); 83 | } else { 84 | try { 85 | PKEntry entry = keyCache.get(alias, new Callable() { 86 | public PKEntry call() throws Exception { 87 | PrivateKey pk = original.getPrivateKey(alias, password); 88 | return new PKEntry(pk, hash(password)); 89 | } 90 | }); 91 | if (!Arrays.equals(hash(password), entry.passHash)) 92 | throw new UnrecoverableKeyException("Wrong password!"); 93 | return entry.pk; 94 | } catch (ExecutionException ex) { 95 | Throwable cause = ex.getCause(); 96 | checkException(cause, KeyStoreException.class); 97 | checkException(cause, NoSuchAlgorithmException.class); 98 | checkException(cause, UnrecoverableKeyException.class); 99 | checkException(cause, RuntimeException.class); 100 | throw new RuntimeException(cause); 101 | } 102 | } 103 | } 104 | 105 | @Override 106 | public X509Certificate getX509Certificate(String alias) throws CertificateException, KeyStoreException { 107 | if (certCache == null) { 108 | return original.getX509Certificate(alias); 109 | } else { 110 | try { 111 | return certCache.get(alias); 112 | } catch (ExecutionException ex) { 113 | Throwable cause = ex.getCause(); 114 | checkException(cause, CertificateException.class); 115 | checkException(cause, KeyStoreException.class); 116 | checkException(cause, RuntimeException.class); 117 | throw new RuntimeException(cause); 118 | } 119 | } 120 | } 121 | 122 | @Override 123 | public KeyStore getKeyStore() { 124 | return original.getKeyStore(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /client_1.11/client/example/src/main/java/Example.java: -------------------------------------------------------------------------------- 1 | import org.w3c.dom.Document; 2 | import org.w3c.dom.Element; 3 | import org.xml.sax.InputSource; 4 | import ru.voskhod.smev.message_exchange_service_client.InAttachment; 5 | import ru.voskhod.smev.message_exchange_service_client.MessageExchangeEndpoint; 6 | import ru.voskhod.smev.message_exchange_service_client.PersonalSigner; 7 | import ru.voskhod.smev.message_exchange_service_client.SMEVCertificateStore; 8 | import ru.voskhod.smev.message_exchange_service_client.impl.FileSystemSMEVCertificateStore; 9 | import ru.voskhod.smev.message_exchange_service_client.impl.KeyPersonalSignerImpl; 10 | import ru.voskhod.smev.message_exchange_service_client.intercept.InterceptorStorage; 11 | import ru.voskhod.crypto.DigitalSignatureFactory; 12 | import ru.voskhod.crypto.KeyStoreWrapper; 13 | 14 | import javax.xml.parsers.DocumentBuilder; 15 | import javax.xml.parsers.DocumentBuilderFactory; 16 | import java.io.File; 17 | import java.io.StringReader; 18 | import java.security.PrivateKey; 19 | import java.security.cert.X509Certificate; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public class Example { 24 | 25 | // "JCP" или "DIGT" 26 | private static final String CRYPTO_PROVIDER = "JCP"; 27 | // адрес СМЭВ 28 | private static final String SMEV_URL = "http://10.77.102.150:7500/ws?wsdl"; 29 | // адрес FTP 30 | private static final String FTP_ADDRESS = "10.77.102.144"; 31 | // имя хранилища 32 | private static final String STORE_NAME = "HDImageStore"; 33 | // имя контейнера ЭП-ОВ 34 | private static final String CONTAINER_ALIAS = "LOSKUTOV"; 35 | // пароль для контейнера ЭП-ОВ 36 | private static final String CONTAINER_PASSWORD = "123456"; 37 | // имя контейнера ЭП-СП 38 | private static final String PERSONAL_CONTAINER_ALIAS = "LOSKUTOV"; 39 | // пароль для контейнера ЭП-СП 40 | private static final String PERSONAL_CONTAINER_PASSWORD = "123456"; 41 | 42 | public static void main(String[] args) throws Exception { 43 | 44 | // подготавливаем временные папки 45 | initFileStore(); 46 | 47 | // инициализация JCP 48 | DigitalSignatureFactory.init(CRYPTO_PROVIDER); 49 | KeyStoreWrapper keyStore = DigitalSignatureFactory.getKeyStoreWrapper(); 50 | 51 | // подготовка подписи ЭП-СП 52 | PrivateKey spPrivateKey; 53 | X509Certificate spCertificate; 54 | { 55 | spPrivateKey = keyStore.getPrivateKey(PERSONAL_CONTAINER_ALIAS, PERSONAL_CONTAINER_PASSWORD.toCharArray()); 56 | spCertificate = keyStore.getX509Certificate(PERSONAL_CONTAINER_ALIAS); 57 | } 58 | 59 | // инициализация точки доступа к веб-сервису и подготовка ЭП-ОВ 60 | PrivateKey ovPrivateKey; 61 | X509Certificate ovCertificate; 62 | { 63 | ovPrivateKey = keyStore.getPrivateKey(CONTAINER_ALIAS, CONTAINER_PASSWORD.toCharArray()); 64 | ovCertificate = keyStore.getX509Certificate(CONTAINER_ALIAS); 65 | 66 | } 67 | MessageExchangeEndpoint messageExchange = MessageExchangeEndpoint.create( 68 | SMEV_URL, FTP_ADDRESS, ovPrivateKey, ovCertificate 69 | ); 70 | 71 | // генерация ID сообщения 72 | String messageID = messageExchange.generateMessageID(); 73 | 74 | // подготовка сообщения 75 | Element content; 76 | { 77 | /* 78 | String contentString = 79 | "\n" + 80 | "\n" + 81 | " Это - русский текст\n" + 82 | ""; 83 | */ 84 | String contentString = "Это - русский текст"; 85 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 86 | factory.setNamespaceAware(true); 87 | DocumentBuilder builder = factory.newDocumentBuilder(); 88 | Document document = builder.parse(new InputSource(new StringReader(contentString))); 89 | content = document.getDocumentElement(); 90 | } 91 | 92 | // подготовка вложений 93 | List attachmentList = new ArrayList(); 94 | // TODO тут формируем список вложений, если они есть 95 | 96 | // можно включить трассировку запросов и ответов (по умолчанию выключена) 97 | InterceptorStorage.getRequest().setIntercept(true); 98 | InterceptorStorage.getResponse().setIntercept(true); 99 | 100 | // отправляем сообщение 101 | try { 102 | // используем ЭП-СП: 103 | PersonalSigner signPersonal = new KeyPersonalSignerImpl(spPrivateKey, spCertificate); 104 | messageExchange.sendRequest(messageID, content, signPersonal, null, attachmentList); 105 | } catch (Exception exception) { 106 | // тут могут быть разные exception в зависимости от ответа сервиса 107 | exception.printStackTrace(System.err); 108 | } 109 | 110 | // через InterceptorStorage можно получить конверты запроса и ответа 111 | System.out.println(InterceptorStorage.getRequest().getString()); 112 | System.out.println(InterceptorStorage.getResponse().getString()); 113 | } 114 | 115 | private static void initFileStore() { 116 | // Хранилище сертификатов СМЭВ. 117 | File smevCertificateStoreDir = new File("smev-certificates"); 118 | SMEVCertificateStore smevCertificateStore = new FileSystemSMEVCertificateStore(smevCertificateStoreDir); 119 | SMEVCertificateStore.setInstance(smevCertificateStore); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /client_1.11/client/xml-artifacts/smev-business-process-metadata-1.0.xsd: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | Описание метаданных бизнес-процесса, передаваемых в сообщениях СМЭВ. 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Номер дела. 29 | 30 | 31 | 32 | Признак: в рамках госуслуги или госфункции послан запрос. 33 | 34 | 35 | Код (номер по реестру) госуслуги или госфункции. 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Запрос послан в ходе оказания госуслуги. 45 | 46 | 47 | Запрос послан в ходе выполнения госфункции. 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Запрос с таким Id не найден в БД СМЭВ. 56 | 57 | 58 | Запрос находится в очереди СМЭВ. 59 | 60 | 61 | Обрабатывается поставщиком сервиса. 62 | 63 | 64 | Запрос выполнен или отвергнут поставщиком сервиса; ответ находится в очереди СМЭВ. 65 | 66 | 67 | Запрос отменён потребителем сервиса. 68 | 69 | 70 | Ответ получен потребителем сервиса. 71 | 72 | 73 | 74 | 75 | 76 | 77 | Тип взаимодействия 78 | 79 | 80 | Взаимодействие портала государственных и/или муниципальных услуг с органом власти. 81 | 82 | 83 | Взаимодействие между органами власти. 84 | 85 | 86 | Взаимодействие между различными информационными системами одного органа исполнительной власти через СМЭВ. 87 | 88 | 89 | Взаимодействие информационно-платежного шлюза с поставщиками начислений для оплаты услуг в электронном виде. 90 | 91 | 92 | Взаимодействие информационно-платежного шлюза с системой УНИФО ФК для получения начислений и фактов оплаты для пользователей ПГУ. 93 | 94 | 95 | Взаимодействие ОИВ с системой УНИФО ФК для передачи начислений и получения фактов оплаты. 96 | 97 | 98 | Другие типы взаимодействия. 99 | 100 | 101 | Не удалось определить тип взаимодействия. 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/XMLTransformHelper.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.Element; 5 | import org.w3c.dom.NodeList; 6 | import org.xml.sax.InputSource; 7 | import org.xml.sax.SAXException; 8 | 9 | import javax.xml.parsers.DocumentBuilder; 10 | import javax.xml.parsers.DocumentBuilderFactory; 11 | import javax.xml.parsers.ParserConfigurationException; 12 | import javax.xml.transform.OutputKeys; 13 | import javax.xml.transform.Transformer; 14 | import javax.xml.transform.TransformerConfigurationException; 15 | import javax.xml.transform.TransformerFactory; 16 | import javax.xml.transform.dom.DOMSource; 17 | import javax.xml.transform.stream.StreamResult; 18 | import java.io.*; 19 | import java.util.logging.Level; 20 | import java.util.logging.Logger; 21 | 22 | public class XMLTransformHelper { 23 | 24 | private static final Logger LOGGER = Logger.getLogger(XMLTransformHelper.class.getName()); 25 | 26 | public static String elementToString(Element element) { 27 | return elementToString(element, false); 28 | } 29 | 30 | public static Element findElement(Element parent, String uri, String localName) { 31 | NodeList nl = parent.getElementsByTagNameNS(uri, localName); 32 | if (nl.getLength() > 0) { 33 | return (Element) nl.item(0); 34 | } else { 35 | return null; 36 | } 37 | } 38 | 39 | public static synchronized Transformer getSyncTransformer() throws TransformerConfigurationException { 40 | TransformerFactory tf = TransformerFactory.newInstance(); 41 | return tf.newTransformer(); 42 | } 43 | 44 | public static String elementToString(Element element, boolean omitxmldeclaration) { 45 | try { 46 | DOMSource domSource = new DOMSource(element); 47 | StringWriter writer = new StringWriter(); 48 | StreamResult result = new StreamResult(writer); 49 | 50 | Transformer transformer = getSyncTransformer(); 51 | if (omitxmldeclaration) { 52 | transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 53 | } 54 | transformer.transform(domSource, result); 55 | writer.flush(); 56 | String xml = writer.toString(); 57 | writer.close(); 58 | transformer.reset(); 59 | return xml; 60 | } catch (Exception e) { 61 | LOGGER.log(Level.SEVERE, null, e); 62 | } 63 | return null; 64 | } 65 | 66 | public static String documentToString(Document arg, boolean omitxmldeclaration) { 67 | try { 68 | DOMSource domSource = new DOMSource(arg); 69 | StringWriter writer = new StringWriter(); 70 | StreamResult result = new StreamResult(writer); 71 | 72 | Transformer transformer = getSyncTransformer(); 73 | if (omitxmldeclaration) { 74 | transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 75 | } 76 | transformer.transform(domSource, result); 77 | writer.flush(); 78 | String xml = writer.toString(); 79 | writer.close(); 80 | transformer.reset(); 81 | return xml; 82 | } catch (Exception e) { 83 | LOGGER.log(Level.SEVERE, null, e); 84 | } 85 | return null; 86 | } 87 | 88 | public static void documentToFile(Document arg, File dst, boolean omitxmldeclaration) { 89 | try { 90 | DOMSource domSource = new DOMSource(arg); 91 | OutputStream out = new FileOutputStream(dst); 92 | StreamResult result = new StreamResult(out); 93 | 94 | Transformer transformer = getSyncTransformer(); 95 | if (omitxmldeclaration) { 96 | transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 97 | } 98 | transformer.transform(domSource, result); 99 | out.flush(); 100 | out.close(); 101 | transformer.reset(); 102 | } catch (Exception e) { 103 | LOGGER.log(Level.SEVERE, null, e); 104 | } 105 | } 106 | 107 | public static Document buildDocumentFromString(String xml) { 108 | return buildDocumentFromString(xml, true); 109 | } 110 | 111 | public static Document buildDocumentFromString(String xml, boolean namespaceaware) { 112 | try { 113 | StringReader reader = new StringReader(xml); 114 | InputSource is = new InputSource(); 115 | is.setCharacterStream(reader); 116 | return buildDocumentFromSource(is, namespaceaware); 117 | } catch (Exception ex) { 118 | LOGGER.log(Level.SEVERE, null, ex); 119 | throw new RuntimeException(ex); 120 | } 121 | } 122 | 123 | public static Document buildDocumentFromFile(String fileName) { 124 | return buildDocumentFromFile(fileName, true); 125 | } 126 | 127 | public static Document buildDocumentFromFile(String fileName, boolean namespaceaware) { 128 | try (Reader reader = new FileReader(fileName)) { 129 | InputSource is = new InputSource(); 130 | is.setCharacterStream(reader); 131 | return buildDocumentFromSource(is, namespaceaware); 132 | } catch (Exception ex) { 133 | LOGGER.log(Level.SEVERE, null, ex); 134 | throw new RuntimeException(ex); 135 | } 136 | } 137 | 138 | public static DocumentBuilder getSyncDocumentBuilder(boolean namespaceAware) throws ParserConfigurationException { 139 | return getSyncDocumentBuilder(namespaceAware, false, false); 140 | } 141 | 142 | public static synchronized DocumentBuilder getSyncDocumentBuilder(boolean namespaceAware, boolean coalescing, boolean ignoringElementContentWhitespace) throws ParserConfigurationException { 143 | DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); 144 | domFactory.setNamespaceAware(namespaceAware); 145 | domFactory.setCoalescing(coalescing); 146 | domFactory.setIgnoringElementContentWhitespace(ignoringElementContentWhitespace); 147 | return domFactory.newDocumentBuilder(); 148 | } 149 | 150 | public static Document getXMLDocument(String xml) throws Exception { 151 | DocumentBuilder builder = getSyncDocumentBuilder(true); 152 | 153 | try { 154 | InputSource is = new InputSource(); 155 | StringReader reader = new StringReader(xml); 156 | is.setCharacterStream(reader); 157 | //Parse the document 158 | Document document = builder.parse(is); 159 | builder.reset(); 160 | return document; 161 | } finally { 162 | builder.reset(); // todo: зачем??? 163 | } 164 | } 165 | 166 | public static Document newDocument(boolean namespaceaware) throws Exception { 167 | DocumentBuilder builder = getSyncDocumentBuilder(namespaceaware); 168 | Document doc = builder.newDocument(); 169 | builder.reset(); 170 | return doc; 171 | } 172 | 173 | public static Document buildDocumentFromSource(InputSource is, boolean namespaceaware) throws ParserConfigurationException, SAXException, IOException { 174 | DocumentBuilder builder = getSyncDocumentBuilder(namespaceaware); 175 | //Parse the document 176 | Document doc = builder.parse(is); 177 | builder.reset(); 178 | return doc; 179 | } 180 | 181 | public static String getXMLDocumentNamespace(Element doc) { 182 | return doc.getNamespaceURI(); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /client_1.11/client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 4.0.0 8 | 9 | ru.voskhod.smev 10 | smev-client 11 | 1.0.11 12 | smev-client 13 | 14 | 15 | ${project.basedir}/xml-artifacts 16 | ${project.basedir}/xml-artifacts/transport-service/1.1 17 | smev-message-exchange-service-1.1.wsdl 18 | smev-business-process-metadata-1.0.xsd 19 | smev-iemk-eln-service.xsd 20 | UTF-8 21 | UTF-8 22 | 1.7.5 23 | 24 | 25 | 26 | 27 | 28 | ru.voskhod.crypto 29 | crypto 30 | 1.0-SNAPSHOT 31 | 32 | 33 | 34 | commons-codec 35 | commons-codec 36 | 1.9 37 | 38 | 39 | 40 | com.fasterxml.uuid 41 | java-uuid-generator 42 | 3.1.3 43 | 44 | 45 | 46 | it.sauronsoftware 47 | ftp4j 48 | 1.7.2 49 | 50 | 51 | 52 | org.glassfish.metro 53 | webservices-rt 54 | 2.3 55 | 56 | 57 | 58 | junit 59 | junit 60 | 3.8.1 61 | test 62 | 63 | 64 | 65 | org.apache.tika 66 | tika-core 67 | 1.5 68 | 69 | 70 | 71 | org.slf4j 72 | slf4j-api 73 | 1.7.5 74 | 75 | 76 | 77 | 78 | 79 | 80 | sauronsoftware-github 81 | releases 82 | https://raw.githubusercontent.com/Takuto88/sauronsoftware-maven/master/releases 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.codehaus.mojo 91 | build-helper-maven-plugin 92 | 1.7 93 | 94 | 95 | generate-sources 96 | 97 | add-source 98 | 99 | 100 | 101 | ${project.build.directory}/generated-sources/jaxws-wsimport 102 | ${project.build.directory}/generated-sources/jaxb-import 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-compiler-plugin 112 | 3.1 113 | 114 | 1.7 115 | 1.7 116 | UTF-8 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-source-plugin 123 | 2.2.1 124 | 125 | 126 | package 127 | 128 | jar-no-fork 129 | 130 | 131 | 132 | 133 | 134 | 135 | org.jvnet.jax-ws-commons 136 | jaxws-maven-plugin 137 | 2.2 138 | 139 | 140 | 141 | wsimport 142 | 143 | 144 | ${wsdl.directory} 145 | 146 | ${wsdl.file} 147 | 148 | ${project.build.directory}/jaxws/stale/wsdl.stale 149 | 150 | wsimport-generate-service 151 | generate-sources 152 | 153 | 154 | 155 | ${project.build.directory}/generated-sources/jaxws-wsimport 156 | true 157 | true 158 | true 159 | 2.0 160 | 161 | 162 | 163 | 164 | org.jvnet.jaxb2.maven2 165 | maven-jaxb2-plugin 166 | 0.9.0 167 | 168 | ${schemas.directory} 169 | 170 | ${bp-metadata-schema.file} 171 | 172 | 173 | ${project.build.directory}/generated-sources/jaxb-import 174 | 175 | 176 | 177 | 178 | generate 179 | 180 | 181 | 182 | 183 | 184 | 185 | org.apache.maven.plugins 186 | maven-failsafe-plugin 187 | 2.17 188 | 189 | true 190 | 191 | 192 | 193 | 194 | integration-test 195 | verify 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/test/java/ru/voskhod/crypto/Main.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto; 2 | 3 | public class Main { 4 | 5 | public static final String TEST_M = "2014-08-14T08:19:28.972+04:00"; 6 | public static final String TEST_S = "5dinABQ/YGiLXUgU3reL/KEWBktTFcnmnmYQRpflkdI=q9Ya909nzP4k5pLuVmXNqP3aiHdGZaQ7WaN7bbtseKG6UMBadPODy7g0zI0u96HaG3WfY9SQJXXZ+LyBm6x5aQ==MIIBVzCCAQSgAwIBAgIEdNpCKTAKBgYqhQMCAgMFADAyMRAwDgYDVQQLEwdTWVNURU0xMREwDwYDVQQKEwhlbXUtdGVzdDELMAkGA1UEBhMCUlUwHhcNMTQwNzA4MTMxNzQ1WhcNMTQxMDA2MTMxNzQ1WjAyMRAwDgYDVQQLEwdTWVNURU0xMREwDwYDVQQKEwhlbXUtdGVzdDELMAkGA1UEBhMCUlUwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAQYHKoUDAgIeAQNDAARAgK00AVZSaE71BMMXbUq77hh1/1OKVqWJmO/tkK4nI7cUBeOlTRJiGDwZbd84v97PBN1ISrakO14m+OAqL+1NfDAKBgYqhQMCAgMFAANBAF3Q42Q0Y2w2abVtY7X5twIJHQ+Q8w3PA/KFIRCO+QWT739UFPNTmwUMvFiS9vqIJFUqiqG2cBT1PSP59sJpiNM="; 7 | 8 | public static final String TEST_M_2 = "38e046d2-21f8-11e4-805e-c76716e45fb67855711111GIBDD222222333333444444"; 9 | public static final String TEST_S_2 = "W7natCiXacUz9rGcFuPWsQIodHZ4I4Bn8Jd5I1XYjus=3ycglxn8IkzGzShjKuVxQJUf1lpbt/6zGtlGTKMamJlWvWkgTOLitp5C3RZ69YDT0bUIwTq9iIV6O3tyQrqNYQ==MIIJlTCCCUSgAwIBAgIKSoKHzAAAAA5UoDAIBgYqhQMCAgMwggFjMRgwFgYFKoUDZAESDTEwMjc2MDA3ODc5OTQxGjAYBggqhQMDgQMBARIMMDA3NjA1MDE2MDMwMTQwMgYDVQQJDCvQnNC+0YHQutC+0LLRgdC60LjQuSDQv9GA0L7RgdC/0LXQutGCINC0LjEyMSMwIQYJKoZIhvcNAQkBFhRyb290QG5hbG9nLnRlbnNvci5ydTELMAkGA1UEBhMCUlUxMTAvBgNVBAgMKDc2INCv0YDQvtGB0LvQsNCy0YHQutCw0Y8g0L7QsdC70LDRgdGC0YwxGzAZBgNVBAcMEtCv0YDQvtGB0LvQsNCy0LvRjDEtMCsGA1UECgwk0J7QntCeINCa0L7QvNC/0LDQvdC40Y8g0KLQtdC90LfQvtGAMTAwLgYDVQQLDCfQo9C00L7RgdGC0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YAxEjAQBgNVBAMTCVRFTlNPUkNBMzAeFw0xNDAzMTExNjAyMjZaFw0xNTAzMTExNjAyMjZaMIIBnTELMAkGA1UEBhMCUlUxPjA8BgkqhkiG9w0BCQIML0lOTj03NjA0MTkzODA5L0tQUD03NjA2MDEwMDEvT0dSTj0xMTA3NjA0MDIwMDM5MRowGAYIKoUDA4EDAQESDDAwNzYwNDE5MzgwOTE4MDYGA1UECgwv0JrQnyDQr9CeICfQrdC70LXQutGC0YDQvtC90L3Ri9C5INGA0LXQs9C40L7QvScxODA2BgNVBAMML9Ca0J8g0K/QniAn0K3Qu9C10LrRgtGA0L7QvdC90YvQuSDRgNC10LPQuNC+0L0nMRgwFgYFKoUDZAESDTExMDc2MDQwMjAwMzkxIjAgBgkqhkiG9w0BCQEWE21rbnlhemlrb3ZhQGVyNzYucnUxJDAiBgNVBAkMG9GD0LsuINCf0L7QsdC10LTRiywg0LQuMTbQkTEKMAgGA1UECwwBMDExMC8GA1UECAwoNzYg0K/RgNC+0YHQu9Cw0LLRgdC60LDRjyDQvtCx0LvQsNGB0YLRjDEbMBkGA1UEBwwS0K/RgNC+0YHQu9Cw0LLQu9GMMGMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgEDQwAEQPwlDFgQQKODB3lU+sOrxODPDGUo5/B6EK5JoKcwMH2pbf0VKpEXduoddzUKcWOGNm8ZqvHMGDnNcrNNJnSJL4GjggWYMIIFlDAOBgNVHQ8BAf8EBAMCBPAwgZwGA1UdJQSBlDCBkQYHKoUDBQMwAQYHKoUDAgIiGQYHKoUDAgIiBgYGKoUDA1kWBgYqhQMCFwMGBiqFAwNZFQYIKwYBBQUHAwQGCCqFAwMpAQMEBggrBgEFBQcDAgYHKoUDBQMoAQYGKoUDZAICBggqhQMHAhUBAgYGKoUDA1kYBggqhQMDOgIBBAYHKoUDAgIiGgYIKoUDAzoCAQIwHQYDVR0gBBYwFDAIBgYqhQNkcQIwCAYGKoUDZHEBMBkGCSqGSIb3DQEJDwQMMAowCAYGKoUDAgIVMB0GA1UdDgQWBBRFWwZrUCcMbt6qwBdZECnTcsSf9zCCAaQGA1UdIwSCAZswggGXgBT6MRbojDA4Trnep1UdnoNJg54NCqGCAWukggFnMIIBYzEYMBYGBSqFA2QBEg0xMDI3NjAwNzg3OTk0MRowGAYIKoUDA4EDAQESDDAwNzYwNTAxNjAzMDE0MDIGA1UECQwr0JzQvtGB0LrQvtCy0YHQutC40Lkg0L/RgNC+0YHQv9C10LrRgiDQtC4xMjEjMCEGCSqGSIb3DQEJARYUcm9vdEBuYWxvZy50ZW5zb3IucnUxCzAJBgNVBAYTAlJVMTEwLwYDVQQIDCg3NiDQr9GA0L7RgdC70LDQstGB0LrQsNGPINC+0LHQu9Cw0YHRgtGMMRswGQYDVQQHDBLQr9GA0L7RgdC70LDQstC70YwxLTArBgNVBAoMJNCe0J7QniDQmtC+0LzQv9Cw0L3QuNGPINCi0LXQvdC30L7RgDEwMC4GA1UECwwn0KPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMRIwEAYDVQQDEwlURU5TT1JDQTOCEGecCYbGEAunTcTyVIIpUsswaAYDVR0fBGEwXzA0oDKgMIYuaHR0cDovL3RheDQudGVuc29yLnJ1L2NlcnRlbnJvbGwvdGVuc29yY2EzLmNybDAnoCWgI4YhaHR0cDovL3RlbnNvci5ydS9jYS90ZW5zb3JjYTMuY3JsMIHbBggrBgEFBQcBAQSBzjCByzA6BggrBgEFBQcwAoYuaHR0cDovL3RheDQudGVuc29yLnJ1L2NlcnRlbnJvbGwvdGVuc29yY2EzLmNydDAtBggrBgEFBQcwAoYhaHR0cDovL3RheDQudGVuc29yLnJ1L3RzcC90c3Auc3JmMC8GCCsGAQUFBzABhiNodHRwOi8vdGF4NC50ZW5zb3IucnUvb2NzcC9vY3NwLnNyZjAtBggrBgEFBQcwAoYhaHR0cDovL3RlbnNvci5ydS9jYS90ZW5zb3JjYTMuY3J0MCsGA1UdEAQkMCKADzIwMTQwMzExMTYxMjAwWoEPMjAxNTA2MTExNjEyMDBaMDYGBSqFA2RvBC0MKyLQmtGA0LjQv9GC0L7Qn9GA0L4gQ1NQIiAo0LLQtdGA0YHQuNGPIDMuNikwggEzBgUqhQNkcASCASgwggEkDCsi0JrRgNC40L/RgtC+0J/RgNC+IENTUCIgKNCy0LXRgNGB0LjRjyAzLjYpDFMi0KPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAICLQmtGA0LjQv9GC0L7Qn9GA0L4g0KPQpiIg0LLQtdGA0YHQuNC4IDEuNQxP0KHQtdGA0YLQuNGE0LjQutCw0YIg0YHQvtC+0YLQstC10YLRgdGC0LLQuNGPIOKEliDQodCkLzEyMS0xODU5INC+0YIgMTcuMDYuMjAxMgxP0KHQtdGA0YLQuNGE0LjQutCw0YIg0YHQvtC+0YLQstC10YLRgdGC0LLQuNGPIOKEliDQodCkLzEyOC0xODIyINC+0YIgMDEuMDYuMjAxMjAIBgYqhQMCAgMDQQAGDmwV/q6+sabJXBFJps1r7VtVDiGebtncitlZatmrpxyOZv2N2WHJysAC2PqiXia7Z6h/AMsb8BlaRsUI5SA/"; 10 | 11 | public static void main(String[] args) throws Exception { 12 | DigitalSignatureFactory.init("DIGT"); 13 | DigitalSignatureProcessor dsp = DigitalSignatureFactory.getDigitalSignatureProcessor(); 14 | // KeyStoreWrapper ksw = DigitalSignatureFactory.getKeyStoreWrapper(); 15 | // Element e = XMLTransformHelper.buildDocumentFromString(TEST_SIGN).getDocumentElement(); 16 | /*dsp.signXMLDSigEnveloped(e, ksw.getPrivateKey("REGISTRY\\\\LOSKUTOV2", "123456".toCharArray()), ksw.getX509Certificate("REGISTRY\\\\LOSKUTOV2")); 17 | System.out.println(XMLTransformHelper.elementToString(e));*/ 18 | // System.out.println(XMLTransformHelper.elementToString(dsp.signXMLDSigDetached(e, null, ksw.getPrivateKey("REGISTRY\\\\LOSKUTOV2", "123456".toCharArray()), ksw.getX509Certificate("REGISTRY\\\\LOSKUTOV2")))); 19 | // DigitalSignatureFactory.getDigitalSignatureProcessor().validateXMLDSigEnvelopedSignature(XMLTransformHelper.buildDocumentFromString(TEST_FALSE).getDocumentElement()); 20 | DigitalSignatureFactory.getDigitalSignatureProcessor().validateXMLDSigDetachedSignature(XMLTransformHelper.buildDocumentFromString(TEST_M_2).getDocumentElement(), XMLTransformHelper.buildDocumentFromString(TEST_S_2).getDocumentElement()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/DigitalSignatureFactory.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto; 2 | 3 | import org.apache.xml.security.algorithms.JCEMapper; 4 | import org.apache.xml.security.algorithms.SignatureAlgorithm; 5 | import org.apache.xml.security.transforms.Transform; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Element; 8 | import ru.voskhod.crypto.exceptions.SigLibInitializationException; 9 | import ru.voskhod.crypto.impl.CacheOptions; 10 | import ru.voskhod.crypto.impl.CachingKeyStoreWrapper; 11 | import ru.voskhod.crypto.impl.DigitalSignatureProcessorImpl; 12 | import ru.voskhod.crypto.impl.SmevTransformSpi; 13 | import ru.voskhod.crypto.impl.csp_tj.TrustedKeyStoreWrapperCSP; 14 | import ru.voskhod.crypto.impl.jcp.KeyStoreWrapperJCP; 15 | 16 | import javax.xml.parsers.DocumentBuilderFactory; 17 | 18 | public class DigitalSignatureFactory { 19 | 20 | private static String providerName = null; 21 | private static volatile DigitalSignatureProcessor processor = null; 22 | private static volatile KeyStoreWrapper keyStoreWrapper = null; 23 | 24 | public static final String CSP_TJ_PROVIDER_NAME = "DIGT"; 25 | public static final String JCP_PROVIDER_NAME = "JCP"; 26 | 27 | public static synchronized void init(String providerName) throws SigLibInitializationException { 28 | if (processor == null) { 29 | if (providerName == null) { 30 | throw new IllegalArgumentException("Метод вызван впервые. Должно быть задано имя провайдера"); 31 | } 32 | try { 33 | if (CSP_TJ_PROVIDER_NAME.equals(providerName)) { 34 | initXmlSec("com.digt.trusted.xmlsig.SignatureStraightGostR34102001GostR3411"); 35 | keyStoreWrapper = new TrustedKeyStoreWrapperCSP(); 36 | } else if (JCP_PROVIDER_NAME.equals(providerName)) { 37 | initXmlSec("ru.CryptoPro.JCPxml.xmldsig.SignatureGostR34102001$SignatureGostR34102001GostR3411"); 38 | keyStoreWrapper = new KeyStoreWrapperJCP(); 39 | } else { 40 | throw new SigLibInitializationException("Процессор для запрошенного провайдера не найден!"); 41 | } 42 | } catch (SigLibInitializationException e) { 43 | throw e; 44 | } catch (Exception e) { 45 | throw new SigLibInitializationException("Не удалось инищиализировать фабрику!", e); 46 | } 47 | DigitalSignatureFactory.providerName = providerName; 48 | processor = new DigitalSignatureProcessorImpl(); 49 | } else { 50 | if (!DigitalSignatureFactory.providerName.equals(providerName)) { 51 | throw new SigLibInitializationException("Процессор уже инициализирован для криптопровайдера: " + DigitalSignatureFactory.providerName + "!"); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * Все что происходит здесь - магия. В теории этого делать не нужно, т.к. необходимый конфиг лежит внутри Trusted Java, но без этого не работает. 58 | * Желающие могут разобратся. 59 | * 60 | * @throws ru.voskhod.crypto.exceptions.SigLibInitializationException В слуючае если произошли проблемы. 61 | */ 62 | private static void initXmlSec(String algorithmClassName) throws SigLibInitializationException { 63 | try { 64 | 65 | // При формировании элемента Signature будут убраны все разрывы между элементами. 66 | System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true"); 67 | 68 | // Регистрируем реализации алгоритмов в xmlsec. 69 | try { 70 | Class.forName(algorithmClassName); 71 | SignatureAlgorithm.providerInit(); 72 | SignatureAlgorithm.register(DigitalSignatureProcessorImpl.XMLDSIG_SIGN_METHOD, algorithmClassName); 73 | } catch (Exception e) { 74 | throw new SigLibInitializationException("Не удалось зарегистрировать алгоритм: " + DigitalSignatureProcessorImpl.XMLDSIG_SIGN_METHOD + "/" + algorithmClassName + ". Убедитесь что выбраный провайдер действительно установлен!", e); 75 | } 76 | 77 | // Готовим конфиг маппинга алгоритмов для JCEMapper. 78 | String NameSpace = "http://www.xmlsecurity.org/NS/#configuration"; 79 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 80 | Document doc = dbf.newDocumentBuilder().newDocument(); 81 | Element root = doc.createElementNS(NameSpace, "JCEAlgorithmMappings"); 82 | Element algs = doc.createElementNS(NameSpace, "Algorithms"); 83 | Element el1 = doc.createElementNS(NameSpace, "Algorithm"); 84 | // Подпись по ГОСТу. 85 | el1.setAttribute("URI", DigitalSignatureProcessorImpl.DIGEST_METHOD); 86 | el1.setAttribute("Description", "GOST R 3411 Digest"); 87 | el1.setAttribute("AlgorithmClass", "MessageDigest"); 88 | el1.setAttribute("RequirementLevel", "OPTIONAL"); 89 | el1.setAttribute("JCEName", "GOST3411"); 90 | algs.appendChild(el1); 91 | Element el2 = doc.createElementNS(NameSpace, "Algorithm"); 92 | el2.setAttribute("URI", DigitalSignatureProcessorImpl.XMLDSIG_SIGN_METHOD); 93 | el2.setAttribute("Description", "GOST R 34102001 Digital Signature Algorithm with GOST R 3411 Digest"); 94 | el2.setAttribute("AlgorithmClass", "Signature"); 95 | el2.setAttribute("RequirementLevel", "OPTIONAL"); 96 | el2.setAttribute("JCEName", "GOST3411withGOST3410EL"); 97 | algs.appendChild(el2); 98 | // SAML отправка. 99 | Element el3 = doc.createElementNS(NameSpace, "Algorithm"); 100 | el3.setAttribute("URI", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"); 101 | el3.setAttribute("Description", "RSA Signature with SHA-1 message digest"); 102 | el3.setAttribute("AlgorithmClass", "Signature"); 103 | el3.setAttribute("RequirementLevel", "RECOMMENDED"); 104 | el3.setAttribute("JCEName", "SHA1withRSA"); 105 | algs.appendChild(el3); 106 | // SAML получение. 107 | Element el4 = doc.createElementNS(NameSpace, "Algorithm"); 108 | el4.setAttribute("URI", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"); 109 | el4.setAttribute("Description", "Key Transport RSA-OAEP"); 110 | el4.setAttribute("AlgorithmClass", "KeyTransport"); 111 | el4.setAttribute("RequirementLevel", "REQUIRED"); 112 | el4.setAttribute("RequiredKey", "RSA"); 113 | el4.setAttribute("JCEName", "RSA/ECB/OAEPWithSHA1AndMGF1Padding"); 114 | algs.appendChild(el4); 115 | Element el5 = doc.createElementNS(NameSpace, "Algorithm"); 116 | el5.setAttribute("URI", "http://www.w3.org/2001/04/xmlenc#aes128-cbc"); 117 | el5.setAttribute("Description", "Block encryption using AES with a key length of 128 bit"); 118 | el5.setAttribute("AlgorithmClass", "BlockEncryption"); 119 | el5.setAttribute("RequirementLevel", "REQUIRED"); 120 | el5.setAttribute("KeyLength", "128"); 121 | el5.setAttribute("RequiredKey", "AES"); 122 | el5.setAttribute("JCEName", "AES/CBC/ISO10126Padding"); 123 | algs.appendChild(el5); 124 | // SAML и когда-то потом еще нужен. 125 | root.appendChild(algs); 126 | doc.appendChild(root); 127 | Element el6 = doc.createElementNS(NameSpace, "Algorithm"); 128 | el6.setAttribute("URI", "http://www.w3.org/2000/09/xmldsig#sha1"); 129 | el6.setAttribute("Description", "SHA-1 message digest"); 130 | el6.setAttribute("AlgorithmClass", "MessageDigest"); 131 | el6.setAttribute("RequirementLevel", "REQUIRED"); 132 | el6.setAttribute("JCEName", "SHA-1"); 133 | algs.appendChild(el6); 134 | // Обязательная инициализация xmlsec. 135 | org.apache.xml.security.Init.init(); 136 | 137 | // Реализация дополнительной трансформации. 138 | Transform.register(SmevTransformSpi.ALGORITHM_URN, SmevTransformSpi.class.getName()); 139 | 140 | // Передаем собраный конфиг. 141 | JCEMapper.init(root); 142 | 143 | } catch (SigLibInitializationException e) { 144 | throw e; 145 | } catch (Exception e) { 146 | throw new SigLibInitializationException("Возникли проблемы при инициализации XmlSec!", e); 147 | } 148 | } 149 | 150 | public static DigitalSignatureProcessor getDigitalSignatureProcessor() throws SigLibInitializationException { 151 | DigitalSignatureProcessor p = processor; 152 | if (p == null) { 153 | throw new SigLibInitializationException("Перед использованием фабрику необходимо инициализировать!"); 154 | } 155 | return p; 156 | } 157 | 158 | public static KeyStoreWrapper getKeyStoreWrapper() throws SigLibInitializationException { 159 | return getKeyStoreWrapper(null); 160 | } 161 | 162 | public static KeyStoreWrapper getKeyStoreWrapper(CacheOptions options) throws SigLibInitializationException { 163 | KeyStoreWrapper ks = keyStoreWrapper; 164 | if (ks == null) { 165 | throw new SigLibInitializationException("Перед использованием фабрику необходимо инициализировать!"); 166 | } 167 | if (options == null) { 168 | return ks; 169 | } else { 170 | return new CachingKeyStoreWrapper(ks, options); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/PKCS7Tools.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | import org.bouncycastle.asn1.ASN1ObjectIdentifier; 4 | import org.bouncycastle.asn1.DEROctetString; 5 | import org.bouncycastle.cert.X509CertificateHolder; 6 | import org.bouncycastle.cms.CMSSignedData; 7 | import org.bouncycastle.cms.SignerInformation; 8 | import org.bouncycastle.cms.SignerInformationStore; 9 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 10 | import ru.voskhod.crypto.exceptions.SignatureValidationException; 11 | import sun.security.pkcs.PKCS7; 12 | import sun.security.pkcs.PKCS9Attribute; 13 | import sun.security.pkcs.PKCS9Attributes; 14 | import sun.security.util.ObjectIdentifier; 15 | import sun.security.x509.AlgorithmId; 16 | import sun.security.x509.X500Name; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.security.*; 23 | import java.security.cert.CertificateFactory; 24 | import java.security.cert.X509Certificate; 25 | import java.util.ArrayList; 26 | import java.util.Collection; 27 | import java.util.List; 28 | 29 | @SuppressWarnings("unchecked") 30 | public class PKCS7Tools { 31 | 32 | public static byte[] signPKCS7SunSecurity(byte[] digestedContent, PrivateKey privateKey, X509Certificate certificate) throws SignatureProcessingException { 33 | try { 34 | 35 | // Данные для подписи. 36 | PKCS9Attribute[] authenticatedAttributeList = { 37 | new PKCS9Attribute(PKCS9Attribute.CONTENT_TYPE_OID, sun.security.pkcs.ContentInfo.DATA_OID), 38 | new PKCS9Attribute(PKCS9Attribute.SIGNING_TIME_OID, new java.util.Date()), 39 | new PKCS9Attribute(PKCS9Attribute.MESSAGE_DIGEST_OID, digestedContent) 40 | }; 41 | PKCS9Attributes authenticatedAttributes = new PKCS9Attributes(authenticatedAttributeList); 42 | // Подписываем. 43 | byte[] signedAttributes = sign(privateKey, authenticatedAttributes.getDerEncoding()); 44 | 45 | // SignerInfo. 46 | java.math.BigInteger serial = certificate.getSerialNumber(); 47 | sun.security.pkcs.SignerInfo si = new sun.security.pkcs.SignerInfo( 48 | new X500Name(certificate.getIssuerDN().getName()), // X500Name issuerName, 49 | serial, //x509.getSerialNumber(), // BigInteger serial, 50 | AlgorithmId.get(JCPCMSTools.DIGEST_OID), //JCPCMSTools.GOST_DIGEST_NAME), // AlgorithmId digestAlgorithmId, 51 | authenticatedAttributes, // PKCS9Attributes authenticatedAttributes, 52 | new AlgorithmId(new ObjectIdentifier(JCPCMSTools.SIGN_OID)), // AlgorithmId digestEncryptionAlgorithmId, 53 | signedAttributes, // byte[] encryptedDigest, 54 | null); // PKCS9Attributes unauthenticatedAttributes) { 55 | sun.security.pkcs.SignerInfo[] signerInfos = {si}; 56 | 57 | // Сертификат. 58 | X509Certificate[] certificates = {certificate}; 59 | 60 | // Алгоритм подписи. 61 | AlgorithmId[] digestAlgorithmIds = {AlgorithmId.get(JCPCMSTools.DIGEST_OID)}; 62 | 63 | sun.security.pkcs.ContentInfo contentInfo = new sun.security.pkcs.ContentInfo(sun.security.pkcs.ContentInfo.DATA_OID, null); 64 | 65 | // Собираем все вместе и пишем в стрим. 66 | PKCS7 p7 = new PKCS7(digestAlgorithmIds, contentInfo, certificates, signerInfos); 67 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 68 | p7.encodeSignedData(bos); 69 | 70 | return bos.toByteArray(); 71 | 72 | } catch (Exception e) { 73 | throw new SignatureProcessingException(e); 74 | } 75 | } 76 | 77 | private static byte[] sign(PrivateKey key, byte[] data) throws SignatureProcessingException { 78 | try { 79 | Signature signer = Signature.getInstance(JCPCMSTools.GOST_EL_SIGN_NAME); 80 | signer.initSign(key); 81 | signer.update(data); 82 | return signer.sign(); 83 | } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex) { 84 | throw new SignatureProcessingException(ex); 85 | } 86 | } 87 | 88 | public static X509Certificate verifyPKCS7BcProv(byte[] argDigestedData, byte[] signedDataByteArray) throws SignatureValidationException { 89 | try { 90 | 91 | // Загоняем подписанные данные в объект. 92 | CMSSignedData signedData = new CMSSignedData(signedDataByteArray); 93 | // Создаем фабрику сертификатов. 94 | CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); 95 | // Вытягиваем все сертификаты. 96 | List certificates = new ArrayList<>(); 97 | for (X509CertificateHolder holder : (Collection) signedData.getCertificates().getMatches(null)) { 98 | certificates.add((X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(holder.getEncoded()))); 99 | } 100 | 101 | // Сертификат, который будет возвращен из метода 102 | X509Certificate certificateToReturn = null; 103 | 104 | // Листаем подписантов. 105 | SignerInformationStore signers = signedData.getSignerInfos(); 106 | for (SignerInformation signer : (Collection) signers.getSigners()) { 107 | 108 | // Проверяем что подписанные данные есть. 109 | if (signer.getSignedAttributes() == null) { 110 | throw new SignatureValidationException("Подпись в формате PKCS#7 не содержет подписанных данных!"); 111 | } 112 | // Извлекаем дайджест, использованный при подписи, сравниваем с актуальным. 113 | org.bouncycastle.asn1.cms.Attribute attribute = signer.getSignedAttributes().get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.4")); 114 | DEROctetString oct = (DEROctetString) attribute.getAttributeValues()[0]; 115 | byte[] dig = oct.getOctets(); 116 | if (!java.util.Arrays.equals(argDigestedData, dig)) { 117 | throw new SignatureValidationException("Дайджест не прошел проверку!"); 118 | } 119 | 120 | // Листаем сертификаты. 121 | byte[] signatureAsByteArray = signer.getSignature(); 122 | for (X509Certificate providedCertificate : certificates) { 123 | // Каждый раз обновляем поток isCheckData. 124 | InputStream isCheckData = new ByteArrayInputStream(signer.getEncodedSignedAttributes()); 125 | boolean signatureIsVerified = checkOnCert(isCheckData, providedCertificate, signatureAsByteArray); 126 | // TODO Loskutov Интересно почиму возвращается только первый удачно проверенный сертификат? 127 | if (signatureIsVerified && certificateToReturn == null) { 128 | certificateToReturn = providedCertificate; 129 | } 130 | } 131 | } 132 | 133 | if (certificateToReturn != null) { 134 | return certificateToReturn; 135 | } else { 136 | throw new SignatureValidationException("Подпись не прошла проверку по сертификату."); 137 | } 138 | } catch (SignatureValidationException e) { 139 | throw e; 140 | } catch (Exception e) { 141 | throw new SignatureValidationException(e); 142 | } 143 | } 144 | 145 | public static boolean checkOnCert(InputStream isCheckData, X509Certificate certificate, byte[] argSignatureAsByteArray) throws SignatureProcessingException { 146 | try { 147 | Signature signature; 148 | try { 149 | signature = Signature.getInstance(JCPCMSTools.GOST_EL_SIGN_NAME); 150 | signature.initVerify(certificate); 151 | } catch (InvalidKeyException e) { 152 | throw new SignatureProcessingException("Открытый ключ поврежден.", e); 153 | } catch (NoSuchAlgorithmException e) { 154 | throw new SignatureProcessingException("Не поддерживается алгоритм подписи " + JCPCMSTools.GOST_EL_SIGN_NAME + ". Убедитесь, что установлен нужный криптопровайдер.", e); 155 | } 156 | 157 | try { 158 | byte[] localBuffer = new byte[4096]; 159 | for (int readBytesCount; (readBytesCount = isCheckData.read(localBuffer)) > 0; ) { 160 | signature.update(localBuffer, 0, readBytesCount); 161 | } 162 | } catch (SignatureException e) { 163 | throw new SignatureProcessingException("Сбой при генерации message digest.", e); 164 | } catch (IOException e) { 165 | throw new SignatureProcessingException("Невозможно прочитать подписываемые данные из потока.", e); 166 | } 167 | 168 | try { 169 | return signature.verify(argSignatureAsByteArray); 170 | } catch (SignatureException e) { 171 | throw new SignatureProcessingException("Сбой на фазе верификации ЭЦП.", e); 172 | } 173 | 174 | } finally { 175 | try { 176 | isCheckData.close(); 177 | } catch (IOException e) { 178 | // TODO Loskutov Нужен ворнинг. 179 | // Ignore. 180 | } 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/MessageExchangeHelper.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client; 2 | 3 | import com.fasterxml.uuid.EthernetAddress; 4 | import com.fasterxml.uuid.UUIDTimer; 5 | import com.fasterxml.uuid.impl.TimeBasedGenerator; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Element; 8 | import ru.voskhod.smev.message_exchange.autogenerated.types.v1_1.GetRequestResponse; 9 | import ru.voskhod.smev.message_exchange.autogenerated.types.v1_1.GetResponseResponse; 10 | import ru.voskhod.smev.message_exchange.autogenerated.types.v1_1.GetStatusResponse; 11 | import ru.voskhod.smev.message_exchange_service_client.datatypes.MessageTypeEnum; 12 | 13 | import javax.xml.bind.JAXBException; 14 | import javax.xml.bind.Marshaller; 15 | import javax.xml.datatype.XMLGregorianCalendar; 16 | import javax.xml.transform.dom.DOMResult; 17 | import java.io.IOException; 18 | import java.util.Date; 19 | import java.util.Random; 20 | 21 | public final class MessageExchangeHelper { 22 | 23 | private static final TimeBasedGenerator uuidGenerator; 24 | 25 | static { 26 | Random random = new Random(System.currentTimeMillis()); 27 | UUIDTimer timer; 28 | try { 29 | timer = new UUIDTimer(random, null); 30 | } catch (IOException e) { 31 | // Will never be thrown. 32 | throw new RuntimeException(e); 33 | } 34 | EthernetAddress addr = EthernetAddress.fromInterface(); 35 | 36 | uuidGenerator = new TimeBasedGenerator(addr, timer); 37 | } 38 | 39 | private MessageExchangeHelper() { 40 | } 41 | 42 | public static String generateUUID() { 43 | return uuidGenerator.generate().toString(); 44 | } 45 | 46 | public static String getMessageSMEVId(GetRequestResponse requestResponse) { 47 | if (requestResponse.getRequestMessage() == null) { 48 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 49 | return null; 50 | } 51 | if (requestResponse.getRequestMessage().getRequest() != null) { 52 | return requestResponse.getRequestMessage().getRequest().getSenderProvidedRequestData().getMessageID(); 53 | } else if (requestResponse.getRequestMessage().getCancel() != null) { 54 | return requestResponse.getRequestMessage().getCancel().getMessageID(); 55 | } else { 56 | return null; 57 | } 58 | } 59 | 60 | public static MessageTypeEnum getMessageType(GetRequestResponse requestResponse) { 61 | if (requestResponse.getRequestMessage() == null) { 62 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 63 | return null; 64 | } 65 | if (requestResponse.getRequestMessage().getRequest() != null) { 66 | return MessageTypeEnum.REQUEST; 67 | } else if (requestResponse.getRequestMessage().getCancel() != null) { 68 | return MessageTypeEnum.CANCEL; 69 | } else { 70 | return null; 71 | } 72 | } 73 | 74 | public static Date getSendingTimestamp(GetRequestResponse requestResponse) { 75 | if (requestResponse.getRequestMessage() == null) { 76 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 77 | return null; 78 | } 79 | XMLGregorianCalendar cal; 80 | if (requestResponse.getRequestMessage().getRequest() != null) { 81 | cal = requestResponse.getRequestMessage().getRequest().getMessageMetadata().getSendingTimestamp(); 82 | } else if (requestResponse.getRequestMessage().getCancel() != null) { 83 | cal = requestResponse.getRequestMessage().getCancel().getMessageMetadata().getSendingTimestamp(); 84 | } else { 85 | return null; 86 | } 87 | 88 | return cal.toGregorianCalendar().getTime(); 89 | } 90 | 91 | public static String getMessageSMEVId(GetResponseResponse responseResponse) { 92 | if (responseResponse.getResponseMessage() == null) { 93 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 94 | return null; 95 | } 96 | if (responseResponse.getResponseMessage().getResponse() != null) { 97 | return responseResponse.getResponseMessage().getResponse().getSenderProvidedResponseData().getMessageID(); 98 | } else { 99 | return null; 100 | } 101 | } 102 | 103 | public static String getRequestMessageSMEVId(GetResponseResponse responseResponse) { 104 | if (responseResponse.getResponseMessage() == null) { 105 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 106 | return null; 107 | } 108 | if (responseResponse.getResponseMessage().getResponse() != null) { 109 | return responseResponse.getResponseMessage().getResponse().getOriginalMessageId(); 110 | } else { 111 | return null; 112 | } 113 | } 114 | 115 | public static MessageTypeEnum getMessageType(GetResponseResponse responseResponse) { 116 | if (responseResponse.getResponseMessage() == null) { 117 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 118 | return null; 119 | } 120 | if (responseResponse.getResponseMessage().getResponse() != null) { 121 | return MessageTypeEnum.RESPONSE; 122 | } else { 123 | return null; 124 | } 125 | } 126 | 127 | public static Date getSendingTimestamp(GetResponseResponse responseResponse) { 128 | if (responseResponse.getResponseMessage() == null) { 129 | // Во входящей очереди нет сообщений, пришла только статистика о загрузке очереди. 130 | return null; 131 | } 132 | XMLGregorianCalendar cal; 133 | if (responseResponse.getResponseMessage().getResponse() != null) { 134 | cal = responseResponse.getResponseMessage().getResponse().getMessageMetadata().getSendingTimestamp(); 135 | } else { 136 | return null; 137 | } 138 | 139 | return cal.toGregorianCalendar().getTime(); 140 | } 141 | 142 | public static String getReturnAddress(GetRequestResponse requestResponse) { 143 | if (requestResponse.getRequestMessage() == null) { 144 | return null; 145 | } 146 | if (requestResponse.getRequestMessage().getRequest() == null) { 147 | return null; 148 | } 149 | return requestResponse.getRequestMessage().getRequest().getReplyTo(); 150 | } 151 | 152 | public static Element getSenderSignature(GetRequestResponse requestResponse) { 153 | if (requestResponse.getRequestMessage() == null) { 154 | return null; 155 | } 156 | if (requestResponse.getRequestMessage().getRequest() != null) { 157 | return requestResponse.getRequestMessage().getRequest().getSenderInformationSystemSignature().getAny(); 158 | } else if (requestResponse.getRequestMessage().getCancel() != null) { 159 | return requestResponse.getRequestMessage().getCancel().getSenderInformationSystemSignature().getAny(); 160 | } else { 161 | return null; 162 | } 163 | } 164 | 165 | public static Element getSenderSignature(GetResponseResponse responseResponse) { 166 | if (responseResponse.getResponseMessage() == null) { 167 | return null; 168 | } 169 | return responseResponse.getResponseMessage().getResponse().getSenderInformationSystemSignature().getAny(); 170 | } 171 | 172 | public static Element marshalContentSignedBySender(GetRequestResponse requestResponse) throws JAXBException { 173 | if (requestResponse.getRequestMessage() == null) { 174 | return null; 175 | } 176 | 177 | Marshaller jaxbMarshaller = MessageExchangeEndpoint.getJAXBMarshaller(); 178 | DOMResult domResult = new DOMResult(); 179 | 180 | if (requestResponse.getRequestMessage().getRequest() != null) { 181 | jaxbMarshaller.marshal(requestResponse.getRequestMessage().getRequest().getSenderProvidedRequestData(), domResult); 182 | } else if (requestResponse.getRequestMessage().getCancel() != null) { 183 | jaxbMarshaller.marshal(requestResponse.getRequestMessage().getCancel().getMessageReference(), domResult); 184 | } else { 185 | return null; 186 | } 187 | 188 | return ((Document) domResult.getNode()).getDocumentElement(); 189 | } 190 | 191 | public static Element marshalContentSignedBySender(GetResponseResponse responseResponse) throws JAXBException { 192 | if (responseResponse.getResponseMessage() == null) { 193 | return null; 194 | } 195 | 196 | Marshaller jaxbMarshaller = MessageExchangeEndpoint.getJAXBMarshaller(); 197 | DOMResult domResult = new DOMResult(); 198 | 199 | if (responseResponse.getResponseMessage().getResponse() != null) { 200 | jaxbMarshaller.marshal(responseResponse.getResponseMessage().getResponse().getSenderProvidedResponseData(), domResult); 201 | return ((Document) domResult.getNode()).getDocumentElement(); 202 | } else { 203 | return null; 204 | } 205 | } 206 | 207 | public static String getMessageSMEVId(GetStatusResponse statusResponse) { 208 | if (statusResponse.getSmevAsyncProcessingMessage() == null) { 209 | // Во входящей очереди нет сообщений 210 | return null; 211 | } 212 | if (statusResponse.getSmevAsyncProcessingMessage().getAsyncProcessingStatus() != null) { 213 | return statusResponse.getSmevAsyncProcessingMessage().getAsyncProcessingStatus().getOriginalMessageId(); 214 | } else { 215 | return null; 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /client_1.11/client/src/main/java/ru/voskhod/smev/message_exchange_service_client/impl/AttachmentBuilder.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.smev.message_exchange_service_client.impl; 2 | 3 | import it.sauronsoftware.ftp4j.*; 4 | import org.apache.commons.codec.binary.Base64; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import ru.voskhod.crypto.PipeInputStream; 8 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 9 | import ru.voskhod.smev.message_exchange.autogenerated.types.basic.v1_1.*; 10 | import ru.voskhod.smev.message_exchange_service_client.*; 11 | 12 | import javax.activation.DataHandler; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public final class AttachmentBuilder { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(AttachmentBuilder.class); 21 | 22 | private static final int MAX_ATTEMPTS = 3; 23 | 24 | private final String ftpAddress; 25 | private final SignatureOperationsClient ovSigner; 26 | private final long directLimit; 27 | 28 | private final List ftpHeaders = new ArrayList<>(); 29 | private final List directContents = new ArrayList<>(); 30 | private final List directHeaders = new ArrayList<>(); 31 | 32 | public AttachmentBuilder(String ftpAddress, SignatureOperationsClient ovSigner, long directLimit) { 33 | this.ftpAddress = ftpAddress; 34 | this.ovSigner = ovSigner; 35 | this.directLimit = directLimit; 36 | } 37 | 38 | private void sendDirect(InAttachment attachment, DigestResult digest) throws SignatureProcessingException, IOException { 39 | byte[] signature; 40 | if (attachment.getPersonalSignature() == null) { 41 | if (digest == null) { 42 | digest = attachment.getDigest(); 43 | } 44 | signature = ovSigner.signPKCS7Detached(digest.getDataDigest()); 45 | } else { 46 | signature = attachment.getPersonalSignature(); 47 | } 48 | 49 | AttachmentHeaderType ah = new AttachmentHeaderType(); 50 | ah.setContentId(attachment.getId()); 51 | ah.setMimeType(attachment.getMimeType()); 52 | ah.setSignaturePKCS7(signature); 53 | directHeaders.add(ah); 54 | 55 | AttachmentContentType ac = new AttachmentContentType(); 56 | ac.setId(attachment.getId()); 57 | ac.setContent(new DataHandler(new AttachmentDataSourceImpl(attachment))); 58 | directContents.add(ac); 59 | } 60 | 61 | private static void checkAttempt(int attempt, T ex) throws T { 62 | logger.warn("Ошибка закачки", ex); 63 | if (attempt >= MAX_ATTEMPTS - 1) { 64 | throw ex; 65 | } 66 | } 67 | 68 | public static void connect(FTPClient ftp, String address) throws FTPException, IOException, FTPIllegalReplyException { 69 | int p = address.lastIndexOf(':'); 70 | if (p < 0) { 71 | ftp.connect(address); 72 | } else { 73 | String host = address.substring(0, p); 74 | String portStr = address.substring(p + 1); 75 | int port; 76 | try { 77 | port = Integer.parseInt(portStr); 78 | } catch (NumberFormatException nfex) { 79 | throw new IOException("Номер порта FTP (" + portStr + ") не является числом", nfex); 80 | } 81 | ftp.connect(host, port); 82 | } 83 | } 84 | 85 | private DigestResult uploadToFTP(String uuid, String fileName, InAttachment attachment) throws FTPException, IOException, FTPIllegalReplyException, FTPDataTransferException, FTPAbortedException, SignatureProcessingException { 86 | for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { 87 | try { 88 | FTPClient ftp = new FTPClient(); 89 | connect(ftp, ftpAddress); 90 | boolean ok = false; 91 | try { 92 | ftp.login("anonymous", "smev"); 93 | try { 94 | ftp.createDirectory(uuid); 95 | } catch (FTPException ex) { 96 | if (attempt == 0) { 97 | logger.warn("Невозможно создать папку FTP", ex); 98 | } 99 | } 100 | ftp.changeDirectory(uuid); 101 | PipeInputStream pipeStream; 102 | try (InputStream inputStream = attachment.getInputStream()) { 103 | pipeStream = SignatureOperationsClient.getDigestCollectingInputStream(inputStream); 104 | long currentSize = 0; 105 | boolean createNew; 106 | if (attempt == 0) { 107 | createNew = true; 108 | } else { 109 | try { 110 | currentSize = ftp.fileSize(fileName); 111 | createNew = false; 112 | } catch (FTPException ex) { 113 | createNew = true; 114 | logger.warn("Невозможно получить размер файла", ex); 115 | } 116 | } 117 | if (createNew) { 118 | ftp.upload(fileName, pipeStream, 0, 0, null); 119 | } else { 120 | ftp.append(fileName, pipeStream, currentSize, null); 121 | } 122 | ok = true; 123 | } 124 | return new DigestResult(pipeStream); 125 | } finally { 126 | try { 127 | ftp.disconnect(ok); 128 | } catch (Exception ex) { 129 | logger.warn("Cannot disconnect from FTP", ex); 130 | } 131 | } 132 | } catch (FTPException ex) { 133 | checkAttempt(attempt, ex); 134 | } catch (FTPIllegalReplyException ex) { 135 | checkAttempt(attempt, ex); 136 | } catch (IOException ex) { 137 | checkAttempt(attempt, ex); 138 | } catch (FTPDataTransferException ex) { 139 | checkAttempt(attempt, ex); 140 | } 141 | } 142 | throw new IllegalStateException(); 143 | } 144 | 145 | private void sendFTP(InAttachment attachment) throws SignatureProcessingException, ClientSideProcessingException { 146 | String attachmentId = attachment.getId(); 147 | String uuid = MessageExchangeHelper.generateUUID(); 148 | try { 149 | DigestResult digestResult = uploadToFTP(uuid, attachmentId, attachment); 150 | 151 | RefAttachmentHeaderType fh = new RefAttachmentHeaderType(); 152 | fh.setUuid(uuid); 153 | byte[] digest = digestResult.getDataDigest(); 154 | fh.setHash(Base64.encodeBase64String(digest)); 155 | fh.setMimeType(attachment.getMimeType()); 156 | byte[] signature; 157 | if (attachment.getPersonalSignature() == null) { 158 | signature = ovSigner.signPKCS7Detached(digest); 159 | } else { 160 | signature = attachment.getPersonalSignature(); 161 | } 162 | fh.setSignaturePKCS7(signature); 163 | ftpHeaders.add(fh); 164 | } catch (IOException ex) { 165 | throw new ClientSideProcessingException(ex); 166 | } catch (FTPException ex) { 167 | throw new ClientSideProcessingException(ex); 168 | } catch (FTPIllegalReplyException ex) { 169 | throw new ClientSideProcessingException(ex); 170 | } catch (FTPAbortedException ex) { 171 | throw new ClientSideProcessingException(ex); 172 | } catch (FTPDataTransferException ex) { 173 | throw new ClientSideProcessingException(ex); 174 | } 175 | } 176 | 177 | public void attach(List attachments) throws ClientSideProcessingException { 178 | try { 179 | List digests = new ArrayList<>(); 180 | long totalSize = 0; 181 | int attachmentCount = 0; 182 | for (InAttachment attachment : attachments) { 183 | if (attachment.getId() == null) { 184 | attachment.setId("__ATT_ID_SMEV_C_AUTOGEN__" + (++attachmentCount)); 185 | } 186 | Long length = attachment.getLength(); 187 | DigestResult digest; 188 | if (length == null) { 189 | digest = attachment.getDigest(); 190 | totalSize += digest.getDataSize(); 191 | } else { 192 | digest = null; 193 | totalSize += length.longValue(); 194 | } 195 | digests.add(digest); 196 | } 197 | 198 | if (totalSize > directLimit) { 199 | for (InAttachment attachment : attachments) { 200 | sendFTP(attachment); 201 | } 202 | } else { 203 | for (int i = 0; i < attachments.size(); i++) { 204 | InAttachment attachment = attachments.get(i); 205 | sendDirect(attachment, digests.get(i)); 206 | } 207 | } 208 | } catch (SignatureProcessingException ex) { 209 | throw new ClientSideProcessingException(ex); 210 | } catch (IOException ex) { 211 | throw new ClientSideProcessingException(ex); 212 | } 213 | } 214 | 215 | public InAttachments getResult() { 216 | AttachmentHeaderList headerList; 217 | AttachmentContentList contentList; 218 | if (directHeaders.size() > 0) { 219 | headerList = new AttachmentHeaderList(); 220 | headerList.getAttachmentHeader().addAll(directHeaders); 221 | contentList = new AttachmentContentList(); 222 | contentList.getAttachmentContent().addAll(directContents); 223 | } else { 224 | headerList = null; 225 | contentList = null; 226 | } 227 | RefAttachmentHeaderList fsAttachmentsList; 228 | if (ftpHeaders.size() > 0) { 229 | fsAttachmentsList = new RefAttachmentHeaderList(); 230 | fsAttachmentsList.getRefAttachmentHeader().addAll(ftpHeaders); 231 | } else { 232 | fsAttachmentsList = null; 233 | } 234 | return new InAttachments(headerList, contentList, fsAttachmentsList); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/DigitalSignatureProcessor.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto; 2 | 3 | import org.w3c.dom.Element; 4 | import ru.voskhod.crypto.exceptions.DocumentIsNotSignedException; 5 | import ru.voskhod.crypto.exceptions.SignatureProcessingException; 6 | import ru.voskhod.crypto.exceptions.SignatureValidationException; 7 | import ru.voskhod.crypto.impl.ValidationResult; 8 | 9 | import java.io.InputStream; 10 | import java.security.PrivateKey; 11 | import java.security.cert.X509Certificate; 12 | import java.util.List; 13 | 14 | /** 15 | * Подписание, проверка ЭЦП. 16 | * Реализации должны быть потокобезопасны. 17 | */ 18 | public interface DigitalSignatureProcessor { 19 | 20 | enum SIG_POSITION {FIRST, LAST} 21 | 22 | /** 23 | * Подписать поток байтов, вернуть ЭЦП в формате PKCS#7. 24 | * Алгоритм расчёта хэш-кода - ГОСТ Р 34.11-94 25 | * Алгоритм подписания - ГОСТ Р 34.10-2001 26 | * 27 | * @param argContent2Sign Поток байтов на подпись. 28 | * @param argPrivateKey Секретный ключ. 29 | * @return Подпись - PKCS#7, сериализованная в поток байтов. 30 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 31 | * Кроме того, выбрасывается, если какой-либо из аргументов - null. 32 | */ 33 | byte[] signPKCS7Detached(InputStream argContent2Sign, PrivateKey argPrivateKey, X509Certificate argUserCertificate) throws SignatureProcessingException; 34 | 35 | /** 36 | * Обернуть InputStream в обёртку, которая будет при чтении потока на лету подсчитывать message digest. 37 | * Смысл операции в том, чтобы избежать дополнительного чтения файла для проверки ЭЦП или подписания. 38 | * После чтения файла (и закрытия потоков), PipeInputStream можно передать методам signPKCS7Detached или validatePKCS7Signature 39 | * с соответствующими сигнатурами. При этом готовый message digest будет взят из PipeInputStream. 40 | * 41 | * @param argStreamToBeWrapped поток, который нужно обернуть. 42 | * @return поток, из которого теперь нужно будет читать там, где раньше читалось из потока - аргумента метода. 43 | */ 44 | PipeInputStream getPipeStream(InputStream argStreamToBeWrapped) throws SignatureProcessingException; 45 | 46 | /** 47 | * @param argDigest Digest, полученный из PipeInputStream, через который прочитали подписываемый файл. 48 | * @param argPrivateKey Секретный ключ. 49 | * @param argUserCertificate Сертификат. 50 | * @return Подпись PKCS#7, сериализованная в поток байтов. 51 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 52 | * Кроме того, выбрасывается, если какой-либо из аргументов - null. 53 | */ 54 | byte[] signPKCS7Detached(byte[] argDigest, PrivateKey argPrivateKey, X509Certificate argUserCertificate) throws SignatureProcessingException; 55 | 56 | /** 57 | * Проверяет ЭЦП формата PKCS#7. 58 | * 59 | * @param argSignedConetnt Контент, на котором проверяется подпись. 60 | * @param argSignature ЭЦП в формате PKCS#7, сериализованная в поток байтов. 61 | * @return Сетрификат, которым был подписан контент. 62 | * @throws ru.voskhod.crypto.exceptions.SignatureValidationException Выбрасывается, если ЭЦП не прошла проверку. 63 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 64 | * Кроме того, выбрасывается, если какой-либо из аргументов - null. 65 | */ 66 | X509Certificate validatePKCS7Signature(InputStream argSignedConetnt, byte[] argSignature) throws SignatureProcessingException, SignatureValidationException; 67 | 68 | /** 69 | * Проверяет ЭЦП формата PKCS#7. 70 | * 71 | * @param argDigest Digest, полученный из PipeInputStream, через который прочитали файл, на котором нужно проверить ЭЦП. 72 | * @param argSignature ЭЦП в формате PKCS#7, сериализованная в поток байтов. 73 | * @return Сетрификат, которым был подписан контент. 74 | * @throws SignatureValidationException Выбрасывается, если ЭЦП не прошла проверку. 75 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 76 | * Кроме того, выбрасывается, если какой-либо из аргументов - null. 77 | *

78 | * USAGE: CORE{SignatureOperationsImpl} 79 | */ 80 | X509Certificate validatePKCS7Signature(byte[] argDigest, byte[] argSignature) throws SignatureProcessingException, SignatureValidationException; 81 | 82 | /** 83 | * Подписать XML-фрагмент по технологии XMLDSig enveloped. 84 | * Cгенерированный {http://www.w3.org/2000/09/xmldsig#}Signature будет добавлен как первый child к подписанному элементу. 85 | * Канонизация - http://www.w3.org/2001/10/xml-exc-c14n# 86 | * Расчёт хэш-кода - ГОСТ Р 34.11-94, http://www.w3.org/2001/04/xmldsig-more#gostr3411 87 | * Подписание - ГОСТ Р 34.10-2001, http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411 88 | * 89 | * @param argDocumentFragment2Sign XML-фрагмент, который необходимо подписать. 90 | * @param argPrivateKey Секретный ключ. 91 | * @param argCertificate Сетрификат. 92 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 93 | * Кроме того, выбрасывается, если какой-либо из аргументов - null. 94 | */ 95 | void signXMLDSigEnveloped(Element argDocumentFragment2Sign, PrivateKey argPrivateKey, X509Certificate argCertificate) throws SignatureProcessingException; 96 | 97 | /** 98 | * Подписать XML-фрагмент по технологии XMLDSig enveloped. 99 | * Сгенерированный {http://www.w3.org/2000/09/xmldsig#}Signature будет добавлен как child к подписанному элементу. 100 | * Канонизация - http://www.w3.org/2001/10/xml-exc-c14n# 101 | * Расчёт хэш-кода - ГОСТ Р 34.11-94, http://www.w3.org/2001/04/xmldsig-more#gostr3411 102 | * Подписание - ГОСТ Р 34.10-2001, http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411 103 | * 104 | * @param argDocumentRoot Корневой элемент XML-документа, фрагмент которого необходимо подписать. 105 | * @param argXPath2Element2Sign XPath-путь к XML-фрагменту, который необходимо подписать. Если равен null, то будет подписан весь argDocumentRoot. 106 | * @param argSignaturePosition признак, будет ли подпись добавлена к подписанному элементу как первый child, или как последний. 107 | * @param argSignatureId signature id, если его нужно вставить в ЭЦП. 108 | * @param argPrivateKey Секретный ключ. 109 | * @param argCertificate Сетрификат. 110 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 111 | * Кроме того, выбрасывается, если какой-либо из обязательных аргументов - null. 112 | */ 113 | void signXMLDSigEnveloped(Element argDocumentRoot, String argXPath2Element2Sign, SIG_POSITION argSignaturePosition, String argSignatureId, PrivateKey argPrivateKey, X509Certificate argCertificate) throws SignatureProcessingException; 114 | 115 | /** 116 | * Подписать XML-фрагмент по технологии XMLDSig detached. 117 | * Этим же способом нужно пользоваться, когда нужна enveloped подпись, но элемент 118 | * {http://www.w3.org/2000/09/xmldsig#}Signature должен быть не child, а более отдалённым descendant 119 | * подписываемого элемента. Присоединение {http://www.w3.org/2000/09/xmldsig#}Signature в нужную точку 120 | * XML-дерева в этом случае делается вручную. 121 | * Канонизация - http://www.w3.org/2001/10/xml-exc-c14n# 122 | * Расчёт хэш-кода - ГОСТ Р 34.11-94, http://www.w3.org/2001/04/xmldsig-more#gostr3411 123 | * Подписание - ГОСТ Р 34.10-2001, http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411 124 | * 125 | * @param argDocument2Sign XML-фрагмент, который необходимо подписать. 126 | * @param argSignatureId signature id, если его нужно вставить в ЭЦП. 127 | * @param argPrivateKey Секретный ключ. 128 | * @param argCertificate Сетрификат. 129 | * @return XML элемент {http://www.w3.org/2000/09/xmldsig#}Signature, представляющий ЭЦП. 130 | *

131 | * USAGE: CORE{SignatureOperationsImpl} 132 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 133 | * Кроме того, выбрасывается, если какой-либо из аргументов - null. 134 | */ 135 | Element signXMLDSigDetached(Element argDocument2Sign, String argSignatureId, PrivateKey argPrivateKey, X509Certificate argCertificate) throws SignatureProcessingException; 136 | 137 | /** 138 | * Проверяет ЭЦП формата XMLDSig enveloped. 139 | * 140 | * @param argSignedContent Подписанный XML-фрагмент. 141 | * @throws ru.voskhod.crypto.exceptions.DocumentIsNotSignedException Выбрасывается, если XML-фрагмент не содержить ЭЦП в формате XMLDSig. 142 | * @throws SignatureValidationException Выбрасывается, если ЭЦП не прошла проверку. 143 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 144 | * Кроме того, выбрасывается, если аргумент - null. 145 | */ 146 | X509Certificate validateXMLDSigEnvelopedSignature(Element argSignedContent) throws SignatureProcessingException, SignatureValidationException, DocumentIsNotSignedException; 147 | 148 | /** 149 | * Проверяет ЭЦП формата XMLDSig detached. 150 | * 151 | * @param argSignedContent Подписанный XML-фрагмент. 152 | * @param argDetachedSignature Элемент {http://www.w3.org/2000/09/xmldsig#}Signature, либо любой его ancestor. 153 | * Если под переданным ancestor находятся несколько элементов {http://www.w3.org/2000/09/xmldsig#}Signature, реализация должна определить, 154 | * какой из них подписывает фрагмент, переданный в argSignedContent. 155 | * @throws SignatureValidationException Выбрасывается, если ЭЦП не прошла проверку. 156 | * @throws SignatureProcessingException Оборачивает любые exceptions, брошенные нижележащим ПО. 157 | * Кроме того, выбрасывается, если аргумент - null. 158 | *

159 | * USAGE: CORE{SignatureOperationsImpl} 160 | */ 161 | X509Certificate validateXMLDSigDetachedSignature(Element argSignedContent, Element argDetachedSignature) throws SignatureProcessingException, SignatureValidationException; 162 | 163 | List validateXMLDSigEnvelopedAllSignature(Element xmlWithSignature) throws SignatureValidationException; 164 | } 165 | -------------------------------------------------------------------------------- /client_1.11/crypto/src/main/java/ru/voskhod/crypto/impl/SmevTransformSpi.java: -------------------------------------------------------------------------------- 1 | package ru.voskhod.crypto.impl; 2 | 3 | import org.apache.xml.security.c14n.CanonicalizationException; 4 | import org.apache.xml.security.c14n.InvalidCanonicalizerException; 5 | import org.apache.xml.security.signature.XMLSignatureInput; 6 | import org.apache.xml.security.transforms.Transform; 7 | import org.apache.xml.security.transforms.TransformSpi; 8 | import org.apache.xml.security.transforms.TransformationException; 9 | import org.xml.sax.SAXException; 10 | 11 | import javax.xml.parsers.ParserConfigurationException; 12 | import javax.xml.stream.*; 13 | import javax.xml.stream.events.*; 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.io.OutputStream; 18 | import java.util.*; 19 | 20 | /** 21 | * Класс, реализующий алгоритм трансформации "urn://smev-gov-ru/xmldsig/transform" для Apache Santuario. 22 | * @author dpryakhin 23 | */ 24 | public class SmevTransformSpi extends TransformSpi { 25 | 26 | public static final String ALGORITHM_URN = "urn://smev-gov-ru/xmldsig/transform"; 27 | private static final String ENCODING_UTF_8 = "UTF-8"; 28 | 29 | // private static Logger logger = LoggerFactory.getLogger(SmevTransformSpi.class); 30 | private static final AttributeSortingComparator attributeSortingComparator = new AttributeSortingComparator(); 31 | 32 | private static final ThreadLocal inputFactory = 33 | new ThreadLocal() { 34 | @Override 35 | protected XMLInputFactory initialValue() { 36 | return XMLInputFactory.newInstance(); 37 | } 38 | }; 39 | 40 | private static final ThreadLocal outputFactory = 41 | new ThreadLocal() { 42 | @Override 43 | protected XMLOutputFactory initialValue() { 44 | return XMLOutputFactory.newInstance(); 45 | } 46 | }; 47 | 48 | private static final ThreadLocal eventFactory = 49 | new ThreadLocal() { 50 | @Override 51 | protected XMLEventFactory initialValue() { 52 | return XMLEventFactory.newInstance(); 53 | } 54 | }; 55 | 56 | /*static { 57 | logger.info("Loading SmevTransformSpi"); 58 | } 59 | 60 | public SmevTransformSpi() { 61 | logger.info("Creating new instance of " + SmevTransformSpi.class.getCanonicalName()); 62 | }*/ 63 | 64 | @Override 65 | protected String engineGetURI() { 66 | return ALGORITHM_URN; 67 | } 68 | 69 | @Override 70 | protected XMLSignatureInput enginePerformTransform(XMLSignatureInput argInput, 71 | OutputStream argOutput, Transform argTransform) throws IOException, 72 | CanonicalizationException, InvalidCanonicalizerException, 73 | TransformationException, ParserConfigurationException, SAXException { 74 | 75 | if (argOutput==null) 76 | return enginePerformTransform(argInput); 77 | else { 78 | process(argInput.getOctetStream(), argOutput); 79 | XMLSignatureInput result = new XMLSignatureInput((byte[]) null); 80 | result.setOutputStream(argOutput); 81 | return result; 82 | } 83 | } 84 | 85 | @Override 86 | protected XMLSignatureInput enginePerformTransform(XMLSignatureInput argInput, 87 | Transform argTransform) throws IOException, CanonicalizationException, 88 | InvalidCanonicalizerException, TransformationException, 89 | ParserConfigurationException, SAXException { 90 | 91 | return enginePerformTransform(argInput); 92 | } 93 | 94 | @Override 95 | protected XMLSignatureInput enginePerformTransform(XMLSignatureInput argInput) 96 | throws IOException, CanonicalizationException, 97 | InvalidCanonicalizerException, TransformationException, 98 | ParserConfigurationException, SAXException { 99 | 100 | ByteArrayOutputStream result = new ByteArrayOutputStream(); 101 | process(argInput.getOctetStream(), result); 102 | byte[] postTransformData = result.toByteArray(); 103 | 104 | return new XMLSignatureInput(postTransformData); 105 | } 106 | 107 | @SuppressWarnings("unchecked") 108 | public void process(InputStream argSrc, OutputStream argDst) throws TransformationException { 109 | 110 | Stack> prefixMappingStack = new Stack<>(); 111 | XMLEventReader src = null; 112 | XMLEventWriter dst = null; 113 | try { 114 | src = inputFactory.get().createXMLEventReader(argSrc, ENCODING_UTF_8); 115 | dst = outputFactory.get().createXMLEventWriter(argDst, ENCODING_UTF_8); 116 | XMLEventFactory factory = eventFactory.get(); 117 | 118 | int prefixCnt = 1; 119 | while(src.hasNext()) { 120 | XMLEvent event = src.nextEvent(); 121 | 122 | if (event.isCharacters()) { 123 | String data = event.asCharacters().getData(); 124 | // Отсекаем whitespace symbols. 125 | if (!data.trim().isEmpty()) { 126 | dst.add(event); 127 | } 128 | continue; 129 | } else if (event.isStartElement()) { 130 | List myPrefixMappings = new LinkedList<>(); 131 | prefixMappingStack.push(myPrefixMappings); 132 | 133 | // Обработка элемента: NS prefix rewriting. 134 | // N.B. Элементы в unqualified form не поддерживаются. 135 | StartElement srcEvent = (StartElement)event; 136 | String nsURI = srcEvent.getName().getNamespaceURI(); 137 | String prefix = findPrefix(nsURI, prefixMappingStack); 138 | 139 | if (prefix == null) { 140 | prefix = "ns" + String.valueOf(prefixCnt++); 141 | myPrefixMappings.add(factory.createNamespace(prefix, nsURI)); 142 | } 143 | StartElement dstEvent = factory.createStartElement(prefix, nsURI, srcEvent.getName().getLocalPart()); 144 | dst.add(dstEvent); 145 | 146 | // == Обработка атрибутов. Два шага: отсортировать, промэпить namespace URI. == 147 | Iterator srcAttributeIterator = srcEvent.getAttributes(); 148 | // Положим атрибуты в list, чтобы их можно было отсортировать. 149 | List srcAttributeList = new LinkedList<>(); 150 | while(srcAttributeIterator.hasNext()) { 151 | srcAttributeList.add(srcAttributeIterator.next()); 152 | } 153 | // Сортировка атрибутов по алфавиту. 154 | Collections.sort(srcAttributeList, attributeSortingComparator); 155 | 156 | // Обработка префиксов. Аналогична обработке префиксов элементов, 157 | // за исключением того, что у атрибут может не иметь namespace. 158 | List dstAttributeList = new LinkedList<>(); 159 | for (Attribute srcAttribute : srcAttributeList) { 160 | String attributeNsURI = srcAttribute.getName().getNamespaceURI(); 161 | String attributeLocalName = srcAttribute.getName().getLocalPart(); 162 | String value = srcAttribute.getValue(); 163 | Attribute dstAttribute; 164 | if (attributeNsURI != null && !"".equals(attributeNsURI)) { 165 | String attributePrefix = findPrefix(attributeNsURI, prefixMappingStack); 166 | if (attributePrefix == null) { 167 | attributePrefix = "ns" + String.valueOf(prefixCnt++); 168 | myPrefixMappings.add(factory.createNamespace(attributePrefix, attributeNsURI)); 169 | } 170 | dstAttribute = factory.createAttribute(attributePrefix, attributeNsURI, attributeLocalName, value); 171 | } else { 172 | dstAttribute = factory.createAttribute(attributeLocalName, value); 173 | } 174 | dstAttributeList.add(dstAttribute); 175 | } 176 | 177 | // Высести namespace prefix mappings для текущего элемента. 178 | // Их порядок детерминирован, т.к. перед мэппингом атрибуты были отсортированы. 179 | // Поэтому дополнительной сотрировки здесь не нужно. 180 | for (Namespace mapping : myPrefixMappings) { 181 | dst.add(mapping); 182 | } 183 | 184 | // Вывести атрибуты. 185 | // N.B. Мы не выводим атрибуты сразу вместе с элементом, используя метод 186 | // XMLEventFactory.createStartElement(prefix, nsURI, localName, List, List), 187 | // потому что при использовании этого метода порядок атрибутов в выходном документе 188 | // меняется произвольным образом. 189 | for (Attribute attr : dstAttributeList) { 190 | dst.add(attr); 191 | } 192 | 193 | continue; 194 | } else if (event.isEndElement()) { 195 | // Гарантируем, что empty tags запишутся в форме , а не в форме . 196 | dst.add(eventFactory.get().createSpace("")); 197 | 198 | // NS prefix rewriting 199 | EndElement srcEvent = (EndElement)event; 200 | String nsURI = srcEvent.getName().getNamespaceURI(); 201 | String prefix = findPrefix(nsURI, prefixMappingStack); 202 | if (prefix == null) { 203 | throw new TransformationException("EndElement: prefix mapping is not found for namespace " + nsURI); 204 | } 205 | 206 | EndElement dstEvent = eventFactory.get().createEndElement(prefix, nsURI, srcEvent.getName().getLocalPart()); 207 | dst.add(dstEvent); 208 | 209 | prefixMappingStack.pop(); 210 | continue; 211 | } else if (event.isAttribute()) { 212 | // Атрибуты обрабатываются в событии startElement. 213 | continue; 214 | } 215 | 216 | // Остальные события (processing instructions, start document, etc.) опускаем. 217 | } 218 | } catch (XMLStreamException e) { 219 | Object[] exArgs = { e.getMessage() }; 220 | throw new TransformationException( 221 | "Can not perform transformation " + ALGORITHM_URN, exArgs, e 222 | ); 223 | } finally { 224 | if (src != null) { 225 | try { 226 | src.close(); 227 | } catch (XMLStreamException e) { 228 | // logger.warn("Can not close XMLEventReader", e); 229 | } 230 | } 231 | if (dst != null) { 232 | try { 233 | dst.close(); 234 | } catch (XMLStreamException e) { 235 | // logger.warn("Can not close XMLEventWriter", e); 236 | } 237 | } 238 | try { 239 | argSrc.close(); 240 | } catch (IOException e) { 241 | // logger.warn("Can not close input stream.", e); 242 | } 243 | if (argDst!=null) { 244 | try { 245 | argDst.close(); 246 | } catch (IOException e) { 247 | // logger.warn("Can not close output stream.", e); 248 | } 249 | } 250 | } 251 | } 252 | 253 | private static String findPrefix(String argNamespaceURI, Stack> argMappingStack) { 254 | if (argNamespaceURI == null) { 255 | throw new IllegalArgumentException("No namespace элементы не поддерживаются."); 256 | } 257 | 258 | for (List elementMappingList : argMappingStack) { 259 | for (Namespace mapping : elementMappingList) { 260 | if (argNamespaceURI.equals(mapping.getNamespaceURI())) { 261 | return mapping.getPrefix(); 262 | } 263 | } 264 | } 265 | return null; 266 | } 267 | 268 | private static class AttributeSortingComparator implements Comparator { 269 | @Override 270 | public int compare(Attribute x, Attribute y) { 271 | String xNS = x.getName().getNamespaceURI(); 272 | String xLocal = x.getName().getLocalPart(); 273 | String yNS = y.getName().getNamespaceURI(); 274 | String yLocal = y.getName().getLocalPart(); 275 | 276 | // Оба атрибута - unqualified. 277 | if (empty(xNS) && empty(yNS)) { 278 | return xLocal.compareTo(yLocal); 279 | } 280 | 281 | // Оба атрибута - qualified. 282 | if (!empty(xNS) && !empty(yNS)) { 283 | // Сначала сравниваем namespaces. 284 | int nsComparisonResult = xNS.compareTo(yNS); 285 | if (nsComparisonResult != 0) { 286 | return nsComparisonResult; 287 | } else { 288 | // Если равны - local names. 289 | return xLocal.compareTo(yLocal); 290 | } 291 | } 292 | 293 | // Один - qualified, второй - unqualified. 294 | if (empty(xNS)) { 295 | return 1; 296 | } else { 297 | return -1; 298 | } 299 | } 300 | 301 | private static boolean empty(String arg) { 302 | return arg == null || "".equals(arg); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /client_1.11/client/xml-artifacts/transport-service/1.1/smev-message-exchange-faults-1.1.xsd: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | Определения элементов, используемых в качестве SOAP fault-сообщений. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Запрос с таким полным именем корневого элемента не поддерживается данной ИС. 31 | Вероятная причина: неверная настройка маршрутизации в СМЭВ. 32 | Действия СМЭВ: Сообщение будет направлено персоналу техподдержки СМЭВ для разбирательства. 33 | 34 | 35 | 36 | 37 | 38 | Local name корневого элемента содержательной части запроса. 39 | 40 | 41 | Namespace URI корневого элемента содержательной части запроса. 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Содержательная часть сообщения (например, в случае запроса - //AcceptRequestRequest/PrimaryContent/element()) 51 | не прошла валидацию по XSD, заявленной для типа запроса. 52 | Действия СМЭВ: Сообщение будет направлено персоналу техподдержки для разбирательства. 53 | N.B. Сообщения могут отвергаться этим способом только по результатам валидации по XML-схеме. 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Сообщение об ошибке валидации. Текстовое содержимое элемента должно содержать сообщение об ошибке, возвращённое валидатором. 62 | 63 | 64 | 65 | 66 | 67 | 68 | Позиция в XML-документе, в которой валидатор обнаружил ошибку. 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | a) При попытке выбрать ответ, указан тип сообщения, не зарегистрированный в СМЭВ 82 | (тип сообщения состоит из /GetResponseRequest/MessageTypeSelector/NamespaceURI и /GetResponseRequest/MessageTypeSelector/RootElementLocalName) 83 | b) При попытке выбрать запрос, указан тип сообщения, либо не зарегистрированный в СМЭВ, либо не принадлежащий текущему клиенту. 84 | Действия клиента: Уточнить тип сообщения, повторить запрос. 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | Сообщение с таким MessageID уже было отправлено ранее. 93 | Возможно в следующих ситуациях: 94 | a) Клиент пытался отправить сообщение, отправка прошла успешно, но в конце взаимодействия HTTP соединение оборвалось, 95 | и клиент не получил ответ от СМЭВ об успешной отправке. 96 | При повторной попытке отправить сообщение клиент получит этот fault. 97 | В этой ситуации нужно прекратить пытаться повторно послать сообщение. 98 | b) Клиент использует алгоритм генерации ID сообщений, не соответствующий МР. 99 | c) Клиент пытается послать несколько сообщений, используя один и тот же ID. 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Идентификатор, присвоенный сообщению отправителем, не является корректным строковым представлением UUID, вариант 1 (см. RFC-4122). 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | Идентификатор (UUID), присвоенный сообщению отправителем, содержит старый timestamp. 116 | Возможно в следующих случаях: 117 | a) в системе отправителя неверно установлено время. 118 | Действия клиента: установить в операционной системе реальное время. 119 | b) в результате очень плохого качества связи, сообщение пытаются отправить в течение двух суток или более. 120 | Действия клиента: сгенерировать новый UUID, присвоить его сообщению, отправить сообщение. 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | Содержимое элемента //AttachmentHeaderList не соответствует содержимому элемента //AttachmentContentList. 129 | Возможные ошибки: 130 | разное количество элементов в списках; 131 | существует элемент //AttachmentHeaderList/AttachmentHeader, у которого атрибут contentId не ссылается либо в никуда, 132 | либо на элемент, отличный от //AttachmentContentList/AttachmentContent. 133 | N.B. Порядок подэлементов //AttachmentHeaderList не обязан соответствовать порядку подэлементов //AttachmentContentList. 134 | Действия СМЭВ: сообщение отброшено. 135 | Действия клиента: Должно быть сформировано новое сообщение, с корректной координацией между заголовками и содержимим вложений. 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | Суммарный размер вложений превысил предел, установленный правилами СМЭВ. 144 | Действия СМЭВ: сообщение отброшено. 145 | Действия клиента: приложенные файлы должны быть уменьшены в размере (например, путём увеличения уровня сжатия JPEG). 146 | Должно быть сформировано новое сообщение, в котором суммарный размер вложений находится в допустимых пределах. 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | Максимальный суммарный размер вложений, разрешённый для передачи в одном СМЭВ-сообщении, в байтах. 155 | 156 | 157 | 158 | 159 | 160 | 161 | Суммарные размер вложений, переданных в отвергнутом сообщении, в байтах. 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Суммарный размер вложений превысил размер оставшейся квоты, установленный правилами СМЭВ. 173 | Действия СМЭВ: сообщение отброшено. 174 | Действия клиента: Освобождение квоты. 175 | После освобождения квоты должно быть переотправлено новое сообщение, в котором суммарный размер вложений находится в допустимых для квоты пределах. 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | Текущее значение оставшейся квоты для СМЭВ-сообщении, в байтах. 184 | 185 | 186 | 187 | 188 | 189 | 190 | Суммарные размер вложений, переданных в отвергнутом сообщении, в байтах. 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | Данному отправителю не разрешена посылка сообщений данному получателю. 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | ЭП-ОВ не прошла проверку. 211 | Действия клиента зависят от кода ошибки верификации. 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | Недопустимый формат SOAP-конверта: отсутствует soap:Header. 220 | Действия клиента: исправить ошибки в коде своей ИС. 221 | 222 | 223 | 224 | 225 | 226 | 227 | ЭП-ОВ не найдена в документе. 228 | Действия клиента: исправить ошибки в коде своей ИС. 229 | 230 | 231 | 232 | 233 | 234 | 235 | ЭП-ОВ подписывает не ту часть СМЭВ-сообщения, которую требуется подписать по спецификации СМЭВ. 236 | Действия клиента: исправить ошибки в коде своей ИС. 237 | 238 | 239 | 240 | 241 | 242 | 243 | Некорректная структура XMLDSig, либо подписанный фрагмент XML-фрагмент не соответствует значению ЭП-ОВ. 244 | Действия клиента: исправить ошибки в коде своей ИС. 245 | 246 | 247 | 248 | 249 | 250 | 251 | Не удалось найти сертификат клиента в ЕСИА. 252 | Действия клиента: обратиться в службу поддержки оператора СМЭВ. 253 | 254 | 255 | 256 | 257 | 258 | 259 | Срок действия сертификата клиента истёк. 260 | Действия клиента: обратиться в удостоверяющий центр за новым сертификатом. 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | Сообщение невозможно принять по причине сбоя в инфраструктуре СМЭВ. 272 | Действия клиента: должны предприниматься повторные попытки доставить сообщение, без ограничения количества попыток. 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | Содержимое посылается через метод SendResponse, 281 | но, согласно реестру типов запросов СМЭВ, содержательная часть сообщения 282 | представляет из себя запрос или заявку, а не ответ. 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | Содержимое посылается через метод SendResponse, 291 | но значение //SendResponseRequest/To/text(), по данным СМЭВ, 292 | не соотевтствует никакому получателю. 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | Отправитель не зарегистрирован в СМЭВ. 301 | Ошибка может также вызываться неправильной настройкой СМЭВ, 302 | а также сменой X-400 имени организации при получении нового сертификата, 303 | используемого для подписания ЭП-ОВ. 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | При попытке отменить запрос, СМЭВ не находит в своём журнале 312 | данных этого запроса. 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | При попытке подтвердить получение сообщения, СМЭВ не находит неподтверждённого сообщения с таким ID. 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | Входящая очередь получателя переполнена. 329 | Обратиться в службу техподдержки СМЭВ, продолжать посылать сообщение. 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /client_1.11/client/xml-artifacts/transport-service/1.1/smev-message-exchange-basic-1.1.xsd: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | Базовые типы. 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Содержательная часть сообщения СМЭВ. 24 | 25 | 26 | 27 | Корневой элемент XML-документа запроса присоединять сюда. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Заголовки файлов, приложенных к СМЭВ-сообщению. 37 | Заголовки отделены от содержимого вложений. 38 | Это нужно потому, что заголовки должны попадать под ЭП-ОВ, 39 | а содержимое - должно не попадать (иначе не будет работать MTOM). 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Файл, приложенный к СМЭВ-сообщению. 53 | Имя файла не передаётся; вложения идентифицируются только идентификаторами внутри сообщения. 54 | 55 | 56 | 57 | 58 | 59 | 60 | Идентификатор вложения. Ссылка на соответствующий //AttachmentContent/@Id 61 | 62 | 63 | 64 | 65 | Тип контента. 66 | 67 | 68 | ЭЦП в формате PKCS#7 detached. Подписывать ключом ЭП-СП. 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Cодержимое вложенных файлов. 77 | У элемента списка, тип base64Binary и наличие атрибута expectedContentTypes - подсказка для frameworks типа JAX-WS передавать содержимое этого элемента по MTOM протоколу. 78 | Кроме того, значение expectedContentTypes="application/octet-stream" - подсказка JAX-WS дать доступ к этому элементу через InputStream/OutputStream. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Идентификатор файла. Ссылка на соответствующий файл. 105 | 106 | 107 | 108 | 109 | Пользователь. 110 | 111 | 112 | Пароль. 113 | 114 | 115 | Имя файла. 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | Содержимое вложенного файла. 127 | Значение атрибута attachmentId должно быть уникально среди всех элементов и атрибутов СМЭВ-сообщения, имеющих тип xs:ID. 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | Заголовки файлов, приложенных к СМЭВ-сообщению. 140 | Заголовки отделены от содержимого вложений. 141 | Это нужно потому, что заголовки должны попадать под ЭП-ОВ, 142 | а содержимое - должно не попадать (иначе не будет работать MTOM). 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | Ссылка на файл, приложенный к СМЭВ-сообщению. 156 | 157 | 158 | 159 | 160 | 161 | 162 | Идентификатор файла. Ссылка на соответствующий файл. 163 | 164 | 165 | 166 | 167 | Хэш файла. 168 | 169 | 170 | Тип контента. 171 | 172 | 173 | ЭЦП в формате PKCS#7 detached. Подписывать ключом ЭП-СП. 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | Строковое представление UUID. 183 | В СМЭВ UUID используются в качестве идентификаторов сообщений. 184 | Идентификаторы присваиваются сообщеням отправителями. 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | Тип для подписанных ссылок на сообщения СМЭВ. 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | Ссылка на сообщение, получение которого подтверждается методом Ack. 207 | Сюда нужно писать Id СМЭВ-сообщения, который берётся 208 | из //GetRequestResponse/.../SenderProvidedRequestData/MessageID/text() либо 209 | из //GetResponseResponse/.../SenderProvidedRequestData/MessageID/text(). 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | true, если ЭП-СМЭВ прошла валидацию и сообщение передано ИС. false, если ЭП-СМЭВ отвергнута, и сообщение проигнорировано. 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | Элекронная подпись в формате XMLDSig. 233 | 234 | 235 | 236 | 237 | 238 | 239 | Собственно подпись, по спецификации XMLDSig. 240 | Объявлена как any для того, чтобы инструменты типа JAXB не генерировали классов для пространства имён 241 | http://www.w3.org/2000/09/xmldsig#. 242 | Это, в свою очередь, нужно потому, что инструменты, генерирующие и проверяющие ЭЦП (напр. Apache Santuario) 243 | работают с DOM-деревьями, а не с JAXB-объектами. 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | Типы вложений, в формате RFC-2046. 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | Селектор, с помощью которого при приёме запроса или ответа 262 | можно задать фильтр по типу запроса (ответа). 263 | Поскольку тип запроса или ответа однозначно определяется полным именем 264 | корневого XML-элемента его бизнес-данных, 265 | селектор представляет из себя структуру для задания этого имени. 266 | Если селектор пуст, это значит, что нужно принять запрос (ответ) 267 | без фильтрации по типам. 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | Текущая дата и время. 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | Запрос с таким Id не найден в БД СМЭВ. 287 | 288 | 289 | Запрос находится в очереди на асинхронную валидацию. 290 | 291 | 292 | Запрос доставляется поставщику. 293 | 294 | 295 | Запрос не прошёл асинхронную валидацию. 296 | 297 | 298 | Обрабатывается поставщиком сервиса. 299 | 300 | 301 | Запрос выполнен или отвергнут поставщиком сервиса; ответ находится в очереди СМЭВ. 302 | 303 | 304 | Запрос отменён потребителем сервиса. 305 | 306 | 307 | Ответ получен потребителем сервиса. 308 | 309 | 310 | 311 | 312 | 313 | 314 | Тип взаимодействия 315 | 316 | 317 | Взаимодействие портала государственных и/или муниципальных услуг с органом власти. 318 | 319 | 320 | Взаимодействие между органами власти. 321 | 322 | 323 | Взаимодействие между различными информационными системами одного органа исполнительной власти через СМЭВ. 324 | 325 | 326 | Взаимодействие информационно-платежного шлюза с поставщиками начислений для оплаты услуг в электронном виде. 327 | 328 | 329 | Взаимодействие информационно-платежного шлюза с системой УНИФО ФК для получения начислений и фактов оплаты для пользователей ПГУ. 330 | 331 | 332 | Взаимодействие ОИВ с системой УНИФО ФК для передачи начислений и получения фактов оплаты. 333 | 334 | 335 | Другие типы взаимодействия. 336 | 337 | 338 | Не удалось определить тип взаимодействия. 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | Тип элемента, который передаёт информацию фактом своего наличия. 385 | 386 | 387 | 388 | 389 | 390 | 391 | --------------------------------------------------------------------------------