機関間電子相互作用(SMEV)のシステムは、サービスの提供と、電子形式での州および地方自治体の機能の実行のためのデジタル環境として考えられていました。
現在、SMEVはその能力を拡大し続けており、インタラクションへの参加者の数が増えています。
商業組織、特に銀行では、サービスを数字に変換してプロセスをシリアル化しようとますます努力しているため、非常に有用であることが判明しました。
この記事では、問い合わせに署名し、SMEVバージョン3.0応答の電子署名を独自に検証する方法と、対処しなければならないいくつかの興味深いニュアンスについて説明します。
こんにちは
質問が生じる場合があります。 なぜ自分で? SMEV 3の
技術ポータル全体がある場合、
- 公開されたすべてのドキュメントとガイドライン
- よくある質問のセクションがあります 、
- SMEV 3クライアントライブラリの現在のバージョンをダウンロードできます。
- 署名付きの完全なメッセージエンベロープの例が提供されています。
- オンラインで、またはSMEVサービススキームへの準拠とその電子署名の有効性の例からメッセージを確認することもできます
もちろん、ポータルは非常に便利です。すべてのヒントとツールを使用できますが、ここでは独自のJavaコードを作成します。
XMLの基本的なセキュリティ標準を実装する
Apache Santuarioプロジェクトライブラリを使用する
XMLAdig、XAdESで動作する独自の情報システムが既にあるという単純な理由から。
CryptoPro JCSPの一部であるライブラリーと同様に、XMLでの作業に加えて、
CryptoPro CSPI CSPの API暗号化機能を提供します。
この場合、SMEV 3電子署名を使用する独自の方法を記述することは、提供された完全なクライアントを展開するよりも適切に見えます。
FSBI研究所Voskhod(2016年3月21日FSUE研究所Voskhodまで)またはその個々のクラスとパッケージの統合。
同時に、クライアントのオープンソースコードを垣間見ることは常に有用であり、その存在だけがシステムの成熟度と高いレベルのサポートを示しています。
ソースデータ分析
SMEV 3ポータルからダウンロードします。
通常のXMLDSigまたは署名、たとえばSMEV 2メッセージエンベロープの作成方法を既に知っている場合、SMEV 3署名のエンベロープがSMEV 2とどのように異なるかに最も関心があります。
SMEV 3エンベロープ
SendRequestRequestNoAttach.xmlの例を開き
ます<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1"> <S:Body> <ns2:SendRequestRequest xmlns:ns3="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/faults/1.1" xmlns:ns2="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1"> <ns:SenderProvidedRequestData Id="SIGNED_BY_CONSUMER" xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" xmlns:ns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" xmlns:ns2="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1"> <ns:MessageID>db0486d0-3c08-11e5-95e2-d4c9eff07b77</ns:MessageID><ns2:MessagePrimaryContent><ns1:BreachRequest xmlns:ns1="urn://x-artefacts-gibdd-gov-ru/breach/root/1.0" xmlns:ns2="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0" xmlns:ns3="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1" Id="PERSONAL_SIGNATURE"> <ns1:RequestedInformation> <ns2:RegPointNum>78557</ns2:RegPointNum> </ns1:RequestedInformation> <ns1:Governance> <ns2:Name> </ns2:Name> <ns2:Code>GIBDD</ns2:Code> <ns2:OfficialPerson> <ns3:FamilyName></ns3:FamilyName> <ns3:FirstName></ns3:FirstName> <ns3:Patronymic></ns3:Patronymic> </ns2:OfficialPerson></ns1:Governance> </ns1:BreachRequest> </ns2:MessagePrimaryContent> <ns:TestMessage/></ns:SenderProvidedRequestData> <ns2:CallerInformationSystemSignature><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"/><ds:Reference URI="#SIGNED_BY_CONSUMER"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:Transform Algorithm="urn://smev-gov-ru/xmldsig/transform"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/><ds:DigestValue>/jXl70XwnttJB5sSokwh8SaVHwo2gjgILSu0qBaLUAo=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>J3746ks34pOcPGQpKzc0sz3n9+gjPtzZbSEEs4c3sTwbtfdaY7N/hxXzEIvXc+3ad9bc35Y8yBhZ/BYbloGt+Q==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIBcDCCAR2gAwIBAgIEHVmVKDAKBgYqhQMCAgMFADAtMRAwDgYDVQQLEwdTWVNURU0xMQwwCgYDVQQKEwNJUzIxCzAJBgNVBAYTAlJVMB4XDTE1MDUwNzEyMTUzMFoXDTE4MDUwNjEyMTUzMFowLTEQMA4GA1UECxMHU1lTVEVNMTEMMAoGA1UEChMDSVMyMQswCQYDVQQGEwJSVTBjMBwGBiqFAwICEzASBgcqhQMCAiMBBgcqhQMCAh4BA0MABEDoWGZlTUWD43G1N7TEm14+QyXrJWProrzoDoCJRem169q4bezFOUODcNooQJNg3PtAizkWeFcX4b93u8fpVy7RoyEwHzAdBgNVHQ4EFgQUaRG++MAcPZvK/E2vR1BBl5G7s5EwCgYGKoUDAgIDBQADQQCg25vA3RJL3kgcJhVOHA86vnkMAtZYr6HBPa7LpEo0HJrbBF0ygKk50app1lzPdZ5TtK2itfmNgTYiuQHX3+nE</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature></ns2:CallerInformationSystemSignature> </ns2:SendRequestRequest> </S:Body> </S:Envelope>
演ductive法を使用すると、次のことがわかります。
この変換により、SMEV 3の正規化に関する独自のルールが広まります。
正規化は、いくつかの可能な表示形式を持つデータを1つの正規化された標準形式にするプロセスです。
XMLエンベロープ内の署名された属性のハッシュを計算して署名する前に、SEEM 3ルールで指定された形式に変換する必要があります。
変換
urnの説明を求めて
:// smev-gov-ru / xmldsig / transform方法論的な推奨事項3.4.0.3
を開きます
メッセージの電子署名の形成に関する規則の4.4.2.1節を
理解しますXMLDSig分離署名形式(https://www.w3.org/TR/xmldsig-core/)
正規化urnに加えて、変換:// smev-gov-ru / xmldsig / transform
フォーマット要件要素間の署名のXML構造では、改行を含むテキストノードは許可されません。
系統的指示のポイント
12.4。 付録4:変換URNのサンプル実装:// SMEV-GOV-RU / XMLDSIG / Transformurn:// smev-gov-ru / xmldsig / transform変換アルゴリズム、Apache Santuarioライブラリのorg.apache.xml.security.transforms.TransformSpiの継承者を実装するJavaクラス
SmevTransformSpi.javaが含まれています。
したがって、署名付きエンベロープSMEV 3の正規化を保証するために、コードでこの変換クラスを使用できます。
この場合の唯一の条件と制限は、署名の生成または検証時にXMLドキュメントを処理するには、Apache Santuarioプロジェクトのorg.apache.xml.security.signature.XMLSignatureを正確に使用する必要があることです。
javax.xml.crypto.dsigまたはru.CryptoPro.JCPxml.xmldsigパッケージのツールを使用しても、そのままではうまくいきません。
SMEV 3の規則に従った署名の準備
Apache Santuarioは当初、GOST暗号化アルゴリズムとCryptoPro暗号化情報保護ツールについて何も知りません。
\ org \ apache \ xml \ security \ resource \ config.xmlファイルのxmlsec-1.5.0.jarライブラリには、外部暗号化アルゴリズムを使用するための設定のみが含まれています。
彼がGOSTの認識と適用を開始するには、初期化する必要があります。
昔ながらの方法は:
CryptoPro JCP(JCSP)の新しいバージョンでは、1行で初期化が実行されます。
ru.CryptoPro.JCPxml.xmldsig.JCPXMLDSigInit.init();
次に、SMEV 3で規定された新しい変換ルールをApache Santuarioに教える必要があります。これを行うには、変換クラスを登録します。
try { Transform.register(SmevTransformSpi.ALGORITHM_URN, SmevTransformSpi.class.getName()); santuarioIgnoreLineBreaks(true); LOGGER.log(Level.INFO, "SmevTransformSpi has been initialized"); } catch (AlgorithmAlreadyRegisteredException e) { LOGGER.log(Level.INFO, "SmevTransformSpi Algorithm already registered: " + e.getMessage()); }
同時に、ガイドラインの要件を直ちに満たします。
フォーマット要件要素間の署名のXML構造では、改行を含むテキストノードは許可されません。
santuarioIgnoreLineBreaks(true); private static final String IGNORE_LINE_BREAKS_FIELD = "ignoreLineBreaks"; private void santuarioIgnoreLineBreaks(Boolean mode) { try { Boolean currMode = mode; AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() { public Boolean run() throws Exception { Field f = XMLUtils.class.getDeclaredField(IGNORE_LINE_BREAKS_FIELD); f.setAccessible(true); f.set(null, currMode); return false; } }); } catch (Exception e) { LOGGER.warning("santuarioIgnoreLineBreaks " + ExceptionUtils.getFullStackTrace(e)); } }
これは、特権ブロックAccessController.doPrivilegedで行われます
SantuarioのignoreLineBreaksプロパティの特定の実装により、リフレクションを介して。
システムプロパティを設定するだけで:
System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
動作しません。
JVMオプションを設定することにより:
-Dcom.sun.org.apache.xml.internal.security.ignoreLineBreaks=true
動作します。
クラス
org.apache.xml.security.utils.XMLUtilsのコードを見ると、ignoreLineBreaksフィールドが静的であり、システムプロパティ
"org.apache.xml.security.ignoreLineBreaks"の特権ブロックで初期化されていることがわかります。
private static boolean ignoreLineBreaks = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { return Boolean.valueOf(Boolean.getBoolean ("org.apache.xml.security.ignoreLineBreaks")); } }).booleanValue(); public static boolean ignoreLineBreaks() { return ignoreLineBreaks; }
このような実装では、メソッドの一部が改行を無視し、別の部分が無視しないように1つのJavaプロセスで柔軟に構成することができません。
つまり、1つのアプリケーションがXMLDsig、SMEV 2、およびSMEV 3署名を実行する場合、Santuarioによって処理されたすべてのXMLドキュメントは出力で改行を失うはずです。
もちろん、このプロパティを使用すると、Apache Santuarioに疑問が生じます。
署名メッセージSMEV 3
すべてがSMEV 3の文書に署名する準備ができています。
署名コードは次のとおりです。
private static final String XMLDSIG_MORE_GOSTR34102001_GOSTR3411 = "http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"; private static final String XMLDSIG_MORE_GOSTR3411 = "http://www.w3.org/2001/04/xmldsig-more#gostr3411"; private static final String CANONICALIZATION_METHOD = "http://www.w3.org/2001/10/xml-exc-c14n#"; private static final String DS_SIGNATURE = "//ds:Signature"; private static final String SIG_ID = "sigID"; private static final String COULD_NOT_FIND_XML_ELEMENT_NAME = "ERROR! Could not find xmlElementName = "; private static final String GRID = "#"; private static final String XML_SIGNATURE_ERROR = "xmlDSignature ERROR: "; try {
主なパラメータは次のとおりです。
byte[] data,
メッセージSMEV 3の署名の検証
署名検証コードは次のとおりです。
private static final QName QNAME_SIGNATURE = new QName("http://www.w3.org/2000/09/xmldsig#", "Signature", "ds"); private static final String SIGNATURE_NOT_FOUND = "Signature not found!"; private static final String SIGNATURE_NOT_VALID = "Signature not valid"; private static final String SMEV_SIGNATURE_PASSED_CORE_VALIDATION = "SmevSignature passed core validation"; private static final String VERIFY_SIGNATURE_ON_XML_IO_EXCEPTION = "Verify signature on XML IOException: "; private static final String VERIFY_SIGNATURE_ON_XML_PARSER_CONFIGURATION_EXCEPTION = "Verify signature on XML ParserConfigurationException: "; private static final String VERIFY_SIGNATURE_ON_XML_SAX_EXCEPTION = "Verify signature on XML SAXException: "; private static final String VERIFY_SIGNATURE_ON_XML_XML_SIGNATURE_EXCEPTION = "Verify signature on XML XMLSignatureException: "; private static final String VERIFY_SIGNATURE_ON_XML_XML_SECURITY_EXCEPTION = "Verify signature on XML XMLSecurityException: "; private static final String ID = "Id"; boolean coreValidity = true; try { DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance(); bf.setNamespaceAware(true); DocumentBuilder b = bf.newDocumentBuilder(); Document doc = b.parse(new InputSource(new ByteArrayInputStream(signedXmlData))); NodeList sigs = doc.getElementsByTagNameNS(QNAME_SIGNATURE.getNamespaceURI(), QNAME_SIGNATURE.getLocalPart()); org.apache.xml.security.signature.XMLSignature sig = null; sigSearch: { for (int i = 0; i < sigs.getLength(); i++) { Element sigElement = (Element) sigs.item(i); String sigId = sigElement.getAttribute(ID); if (sigId != null) { sig = new org.apache.xml.security.signature.XMLSignature(sigElement, ""); break sigSearch; } } throw new XMLSignatureVerificationException(SIGNATURE_NOT_FOUND); } org.apache.xml.security.keys.KeyInfo ki = (org.apache.xml.security.keys.KeyInfo) sig.getKeyInfo(); X509Certificate certificate = ki.getX509Certificate(); if (!sig.checkSignatureValue(certificate.getPublicKey())) { coreValidity = false; LOGGER.log(Level.INFO, SIGNATURE_NOT_VALID); } else { LOGGER.log(Level.INFO, String.format(SMEV_SIGNATURE_PASSED_CORE_VALIDATION)); } } catch (IOException e) { throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_IO_EXCEPTION + ExceptionUtils.getStackTrace(e)); } catch (ParserConfigurationException e) { throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_PARSER_CONFIGURATION_EXCEPTION + ExceptionUtils.getStackTrace(e)); } catch (SAXException e) { throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_SAX_EXCEPTION + ExceptionUtils.getStackTrace(e)); } catch (org.apache.xml.security.signature.XMLSignatureException e) { throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_XML_SIGNATURE_EXCEPTION + ExceptionUtils.getStackTrace(e)); } catch (XMLSecurityException e) { throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_XML_SECURITY_EXCEPTION + ExceptionUtils.getStackTrace(e)); } return coreValidity;
問題。 ハッシュが一致しません
注意!デバッグには、SMEV 3エンベロープSendRequestRequestNoAttach.xmlの例が使用されました。
メッセージに再度署名し、元のメッセージで検証するために、ds:Signature要素が削除されました。
ガイドラインから取られた署名方法と変換SmevTransformSpiが機能したにもかかわらず、出力は署名された文書であり、その署名はSMEV 3ポータルでのオンライン検証として解釈されました。
EP-OVが確認されていません:ESのチェックエラー:ESの整合性に違反しています
なぜ
<ds:DigestValue>e76oVeYGapFDE+PV6glsj0XDjLHydLMd0cSkFPY8fWk=</ds:DigestValue>
元の例と一致しませんでした:
<ds:DigestValue>/jXl70XwnttJB5sSokwh8SaVHwo2gjgILSu0qBaLUAo==</ds:DigestValue>
原因を診断するために、SmevTransformSpiクラスのプロセスメソッドにXMLEventWriterが追加されました。
ByteArrayOutputStream baos = new ByteArrayOutputStream(); XMLEventWriter bdst = outputFactory.get().createXMLEventWriter(baos, ENCODING_UTF_8);
変換のすべての段階の並列分析用。
サインオンする正規化されたXML要素は次のようになりました。
<ns1:SenderProvidedRequestData xmlns:ns1="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" Id="SIGNED_BY_CONSUMER"><ns1:MessageID>db0486d0-3c08-11e5-95e2-d4c9eff07b77</ns1:MessageID><ns2:MessagePrimaryContent xmlns:ns2="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1"><ns3:BreachRequest xmlns:ns3="urn://x-artefacts-gibdd-gov-ru/breach/root/1.0" Id="PERSONAL_SIGNATURE"><ns3:RequestedInformation><ns4:RegPointNum xmlns:ns4="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">78557</ns4:RegPointNum></ns3:RequestedInformation><ns3:Governance><ns4:Name> </ns4:Name><ns4:Code>GIBDD</ns4:Code><ns4:OfficialPerson><ns5:FamilyName xmlns:ns5="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1"></ns5:FamilyName><ns5:FirstName></ns5:FirstName><ns5:Patronymic></ns5:Patronymic></ns4:OfficialPerson></ns3:Governance></ns3:BreachRequest></ns2:MessagePrimaryContent><ns1:TestMessage></ns1:TestMessage></ns1:SenderProvidedRequestData>
ソリューションの検索により、まず、
CryptoProフォーラムで、正規化されたドキュメントは実際とは異なって見える場合があり、それに応じてそのハッシュが異なり、場合によっては正しくなることが示されました。
第二に、彼はそれを
GitHubに持ち込み、古いバージョンのSmevTransformSpiクラスが投稿されました。
変換クラスの古いバージョンは、次の正規化されたドキュメントを作成しました。
<ns1:SenderProvidedRequestData xmlns:ns1="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" Id="SIGNED_BY_CONSUMER"><ns1:MessageID>db0486d0-3c08-11e5-95e2-d4c9eff07b77</ns1:MessageID><ns2:MessagePrimaryContent xmlns:ns2="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1"><ns3:BreachRequest xmlns:ns3="urn://x-artefacts-gibdd-gov-ru/breach/root/1.0" Id="PERSONAL_SIGNATURE"><ns3:RequestedInformation><ns4:RegPointNum xmlns:ns4="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">78557</ns4:RegPointNum></ns3:RequestedInformation><ns3:Governance><ns5:Name xmlns:ns5="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0"> </ns5:Name><ns6:Code xmlns:ns6="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">GIBDD</ns6:Code><ns7:OfficialPerson xmlns:ns7="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0"><ns8:FamilyName xmlns:ns8="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1"></ns8:FamilyName><ns9:FirstName xmlns:ns9="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1"></ns9:FirstName><ns10:Patronymic xmlns:ns10="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1"></ns10:Patronymic></ns7:OfficialPerson></ns3:Governance></ns3:BreachRequest></ns2:MessagePrimaryContent><ns1:TestMessage></ns1:TestMessage></ns1:SenderProvidedRequestData>
これにより、ハッシュが一致し始め、署名が正常に検証されました。
SmevTransformSpiクラスのバージョンを比較すると、デバッグモードの新しい実装で追加されたロギングおよび診断機能に加えて、次のことがわかりました。
if (logger.isDebugEnabled()) { debugStream = new DebugOutputStream(argDst); dst = outputFactory.get().createXMLEventWriter(debugStream, ENCODING_UTF_8); } else { dst = outputFactory.get().createXMLEventWriter(argDst, ENCODING_UTF_8); }
ガイドラインのクラスに必要な行が含まれていないか、タイプミスが含まれています。
行がありません:
prefixMappingStack.pop();
プレフィックスから最初のオブジェクトをスタックから削除します
Stack<List<Namespace>> prefixMappingStack = new Stack<List<Namespace>>();
、これはSmevTransformSpiの誤った操作につながりました。
この行をSmevTransformSpi.javaの新しいバージョンに追加すると、問題が解決しました。
作業中の変換クラスと署名付きのエンベロープは、
github.com / VBurmistrov / Smev3で表示できます。
結果
SMEV 3エンベロープの署名に成功しました。
メッセージは、
電子政府政府サービスのポータルで確認され
ますそして、ネイティブアプリケーションでは: