Im Zusammenhang mit der Umstellung auf Linux wurde es notwendig, eines unserer in C # geschriebenen Serversysteme nach Mono zu portieren. Das System arbeitet mit erweiterten digitalen Signaturen. Eine der Aufgaben, mit denen wir konfrontiert waren, bestand darin, die Leistung von GOST-Zertifikaten von CryptoPro in Mono zu testen. CryptoPro selbst hat CSP für Linux schon seit einiger Zeit implementiert, aber der erste Versuch, es zu verwenden, zeigte, dass die nativen Mono-Kryptografieklassen (ähnlich denen in der Basis .Net - X509Store, X509Certificate2 usw.) nicht nur nicht mit Gastschlüsseln funktionieren, sondern sogar sehe sie nicht in ihren Gewölben. Aus diesem Grund musste die Arbeit mit Kryptografie direkt über die CryptoPro-Bibliotheken verbunden werden.
Installation des Zertifikats
Bevor Sie den Code implementieren, müssen Sie das Zertifikat installieren und sicherstellen, dass es ordnungsgemäß funktioniert.
Installation des ZertifikatsDie CryptoPro CSP-Komponente Version 3.9 wurde auf Centos 7 im Ordner / opt / cprocsp installiert. Um Konflikte zwischen den gleichnamigen Dienstprogrammen mono und CryptoPro (z. B. certmgr) zu vermeiden, wurde der Pfad zum Ordner nicht in die Umgebungsvariablen eingegeben, und alle Dienstprogramme wurden im vollständigen Pfad aufgerufen.
Zunächst definieren wir eine Liste von Lesern:
/opt/cprocsp/bin/amd64/csptest -enum -info -type PP_ENUMREADERS | iconv -f cp1251
Wenn sich kein Leser aus dem Ordner auf der Festplatte (HDIMAGE) in der Liste befindet, fügen Sie Folgendes ein:
/opt/cprocsp/sbin/amd64/cpconfig -hardware reader -add HDIMAGE store
Anschließend können Sie Container der Form '\\. \ HDIMAGE \ {Containername}' erstellen, indem Sie entweder einen neuen Container mit Schlüsseln erstellen:
/opt/cprocsp/bin/amd64/csptest -keyset -provtype 75 -newkeyset -cont '\\.\HDIMAGE\test'
oder indem Sie den Ordner / var / opt / cprocsp / keys / root / {Containername} .000 erstellen, der den Standardsatz von CryptoPro-Containerdateien (* .key, * .mask usw.) enthält.
Danach kann das Zertifikat aus dem Container im Zertifikatspeicher installiert werden:
/opt/cprocsp/bin/amd64/certmgr -inst mMy -cont '\\.\HDIMAGE\{ }'
Das installierte Zertifikat kann mit dem folgenden Befehl angezeigt werden:
/opt/cprocsp/bin/amd64/certmgr -list mMy
Der Betrieb des Zertifikats kann wie folgt überprüft werden:
/opt/cprocsp/bin/amd64/cryptcp – sign -norev -thumbprint {} {} { }
/opt/cprocsp/bin/amd64/cryptcp – verify -norev { }
Wenn mit dem Zertifikat alles in Ordnung ist, können Sie mit der Verbindung im Code fortfahren.
Verbindung im Code
Trotz des Portierungsprozesses nach Linux sollte das System weiterhin in der Windows-Umgebung funktionieren. Daher musste die Arbeit mit Kryptografie äußerlich mit allgemeinen Methoden der Form „Byte [] SignData (Byte [] _arData, X509Certificate2 _pCert)“ durchgeführt werden, die genauso funktionieren sollten unter Linux sowie unter Windows.
Die Analyse der Methoden von Kryptografiebibliotheken erwies sich als erfolgreich, da CryptoPro die Bibliothek "libcapi20.so" implementierte, die die Standardverschlüsselungsbibliotheken Windows - "crypt32.dll" und "advapi32.dll" vollständig nachahmt. Vielleicht natürlich nicht ganz, aber dort stehen alle notwendigen Methoden für die Arbeit mit Kryptographie zur Verfügung, und fast alle funktionieren.
Daher bilden wir zwei statische Klassen "WCryptoAPI" und "LCryptoAPI", von denen jede die erforderlichen Methoden wie folgt importiert:
[DllImport(LIBCAPI20, SetLastError = true)] internal static extern bool CertCloseStore(IntPtr _hCertStore, uint _iFlags);
Die Verbindungssyntax für jede der Methoden kann entweder unabhängig erstellt oder über die Pinvoke- Website erstellt oder aus .NET-Quellen ( CAPISafe- Klasse) kopiert werden . Aus demselben Modul können Sie Konstanten und Strukturen zeichnen, die mit der Kryptografie verbunden sind und deren Vorhandensein das Leben bei der Arbeit mit externen Bibliotheken immer erleichtert.
Und dann bilden wir die statische Klasse „UCryptoAPI“, die je nach System die Methode einer von zwei Klassen aufruft:
internal static bool CertCloseStore(IntPtr _hCertStore, uint _iFlags) { if (fIsLinux) return LCryptoAPI.CertCloseStore(_hCertStore, _iFlags); else return WCryptoAPI.CertCloseStore(_hCertStore, _iFlags); } public static bool fIsLinux { get { int iPlatform = (int) Environment.OSVersion.Platform; return (iPlatform == 4) || (iPlatform == 6) || (iPlatform == 128); } }
Mit den Methoden der UCryptoAPI-Klasse können Sie somit für beide Systeme nahezu einheitlichen Code implementieren.
Zertifikatsuche
Die Arbeit mit Kryptografie beginnt normalerweise mit einer Zertifikatsuche. Dazu gibt es in crypt32.dll zwei CertOpenStore-Methoden (öffnet den angegebenen Zertifikatspeicher) und einen einfachen CertOpenSystemStore (öffnet die persönlichen Zertifikate des Benutzers). Da die Arbeit mit Zertifikaten nicht auf persönliche Benutzerzertifikate beschränkt ist, verbinden wir das erste:
Zertifikatsuche public static int FindCertificateCP(string _pFindValue, out X509Certificate2 _pCert, ref string _sError, StoreLocation _pLocation = StoreLocation.CurrentUser, StoreName _pName = StoreName.My, X509FindType _pFindType = X509FindType.FindByThumbprint, bool _fVerify = false) { _pCert = null; IntPtr hCert = IntPtr.Zero; GCHandle hInternal = new GCHandle(); GCHandle hFull = new GCHandle(); IntPtr hSysStore = IntPtr.Zero; try {
Die Suche erfolgt in mehreren Schritten:
- Lageröffnung;
- Bildung der Datenstruktur, nach der wir suchen;
- Zertifikatsuche;
- falls erforderlich, dann Zertifikatsüberprüfung (in einem separaten Abschnitt beschrieben);
- Schließen des Repositorys und Freigeben der Struktur ab Punkt 2 (da überall mit nicht verwaltetem .NET-Speicher gearbeitet wird, wird nichts unternommen, um ihn zu bereinigen);
Bei der Suche nach Zertifikaten gibt es einige subtile Punkte.
CryptoPro unter Linux funktioniert mit ANSI-Zeichenfolgen und unter Windows mit UTF8:
- Beim Verbinden der Methode zum Öffnen des Speichers unter Linux muss die Art des Marshallings explizit angegeben werden [In, MarshalAs (UnmanagedType.LPStr)].
- Wenn Sie die Suchzeichenfolge übergeben (z. B. mit dem Namen Subject), muss sie in eine Reihe von Bytes mit unterschiedlichen Codierungen konvertiert werden.
- Für alle kryptografischen Konstanten, die je nach Zeichenfolgentyp variieren (z. B. CERT_FIND_SUBJECT_STR_A und CERT_FIND_SUBJECT_STR_W), müssen Sie unter Windows * _W und unter Linux * _A auswählen.
Die MapX509StoreFlags- Methode kann ohne Änderungen direkt aus Microsoft-Quellen übernommen werden. Sie bildet einfach eine endgültige Maske basierend auf .NET-Flags.
Der Wert, nach dem die Suche stattfindet, hängt von der Art der Suche ab (überprüfen Sie mit MSDN nach CertFindCertificateInStore ). Das Beispiel zeigt die beiden am häufigsten verwendeten Optionen - für das Zeichenfolgenformat (Namen Betreff, Aussteller usw.) und binär (Fingerabdruck, Seriennummer).
Das Erstellen eines Zertifikats aus IntPtr unter Windows und Linux ist sehr unterschiedlich. Windows erstellt das Zertifikat auf einfache Weise:
new X509Certificate2(hCert);
Unter Linux müssen Sie in zwei Schritten ein Zertifikat erstellen:
X509Certificate2(new X509Certificate(hCert));
In Zukunft benötigen wir Zugriff auf hCert für die Arbeit, und es müsste im Zertifikatobjekt gespeichert werden. Unter Windows kann es später aus der Handle-Eigenschaft abgerufen werden. Linux konvertiert jedoch die CERT_CONTEXT-Struktur, die dem hCert-Link folgt, in einen Link zur x509_st-Struktur (OpenSSL) und registriert sie im Handle. Daher lohnt es sich, aus X509Certificate2 (im Beispiel ISDP_X509Cert) einen Erben zu erstellen, der hCert in beiden Systemen in einem separaten Feld speichert.
Vergessen Sie nicht, dass dies eine Verknüpfung zu einem Bereich mit nicht verwaltetem Speicher ist und nach Beendigung der Arbeit freigegeben werden muss. Weil in .Net 4.5 ist X509Certificate2 nicht verfügbar - die Reinigung mit der CertFreeCertificateContext-Methode muss im Destruktor durchgeführt werden.
Signaturbildung
Bei der Arbeit mit GOST-Zertifikaten werden fast immer getrennte Signaturen mit einem Unterzeichner verwendet. Um eine solche Signatur zu erstellen, ist ein ziemlich einfacher Codeblock erforderlich:
Signaturbildung public static int SignDataCP(byte[] _arData, X509Certificate2 _pCert, out byte[] _arRes, ref string _sError) { _arRes = new byte[0];
Während der Arbeit der Methode wird eine Struktur mit Parametern gebildet und die Signaturmethode aufgerufen. Mit der Parameterstruktur können Sie Zertifikate in der Signatur speichern, um eine vollständige Kette zu bilden (die Felder cMsgCert und rgpMsgCert, das erste speichert die Anzahl der Zertifikate, die zweite Liste der Links zu den Strukturen dieser Zertifikate).
Die Signaturmethode kann ein oder mehrere Dokumente zum gleichzeitigen Signieren mit einer Signatur empfangen. Dies widerspricht übrigens nicht dem Bundesgesetz 63 und ist sehr praktisch, da es unwahrscheinlich ist, dass sich der Benutzer über die Notwendigkeit freut, mehrmals auf die Schaltfläche „Zeichen“ zu klicken.
Die größte Kuriosität dieser Methode ist, dass sie nicht im Zwei-Aufruf-Modus funktioniert, was typisch für die meisten Bibliotheksmethoden ist, die mit großen Speicherblöcken arbeiten (die erste mit null - gibt die erforderliche Pufferlänge zurück, die zweite füllt den Puffer). Daher ist es notwendig, einen großen Puffer zu erstellen und ihn dann auf seine tatsächliche Länge zu kürzen.
Das einzige ernsthafte Problem ist die Suche nach der OID des beim Signieren verwendeten Hash-Algorithmus (Digest) - in expliziter Form nicht im Zertifikat (es gibt nur den Algorithmus der Signatur selbst). Und wenn Sie es unter Windows mit einer leeren Zeichenfolge angeben können, wird es automatisch erkannt, aber Linux weigert sich zu signieren, wenn der Algorithmus nicht identisch ist.
Es gibt jedoch einen Trick: In den Informationen zum Signaturalgorithmus (Struktur CRYPT_OID_INFO) wird die Signatur-OID in pszOID und die Hash-Algorithmus-ID in Algid gespeichert. Die Umstellung von Algid auf OID ist bereits eine technische Angelegenheit:
Abrufen der OID des Hash-Algorithmus internal static int GetHashAlgoritmOID(IntPtr _hCertHandle, out string _sOID, ref string _sError) { _sOID = ""; IntPtr hHashAlgInfo = IntPtr.Zero; IntPtr hData = IntPtr.Zero; try { CERT_CONTEXT pContext = (CERT_CONTEXT)Marshal.PtrToStructure(_hCertHandle, typeof(CERT_CONTEXT)); CERT_INFO pCertInfo = (CERT_INFO)Marshal.PtrToStructure(pContext.pCertInfo, typeof(CERT_INFO));
Nach sorgfältigem Lesen des Codes können Sie überrascht sein, dass die Algorithmus-ID auf einfache Weise abgerufen wird (CertOIDToAlgId) und die darin enthaltene Oid kompliziert ist (CryptFindOIDInfo). Es wäre logisch anzunehmen, dass entweder beide komplexe oder beide einfache Methoden verwendet werden, und unter Linux funktionieren beide Optionen erfolgreich. Unter Windows ist die schwierige Option, eine Kennung zu erhalten und einfach eine OID zu erhalten, jedoch instabil, sodass ein solch seltsamer Hybrid eine stabile Lösung wäre.
Überprüfung der Unterschrift
Die Überprüfung der Signatur erfolgt in zwei Schritten. Zu Beginn wird die Signatur selbst überprüft und anschließend das Zertifikat, mit dem sie erstellt wurde (Kette, Datum der Unterzeichnung usw.).
Neben der Signatur müssen Sie den zu signierenden Datensatz, die Signaturparameter und die Signatur selbst angeben:
Überprüfung der Unterschrift internal static CRYPT_VERIFY_MESSAGE_PARA GetStdSignVerifyPar() { CRYPT_VERIFY_MESSAGE_PARA pVerifyParams = new CRYPT_VERIFY_MESSAGE_PARA(); pVerifyParams.cbSize = (int)Marshal.SizeOf(pVerifyParams); pVerifyParams.dwMsgEncodingType = UCConsts.PKCS_7_OR_X509_ASN_ENCODING; pVerifyParams.hCryptProv = 0; pVerifyParams.pfnGetSignerCertificate = IntPtr.Zero; pVerifyParams.pvGetArg = IntPtr.Zero; return pVerifyParams; } public static int CheckSignCP(byte[] _arData, byte[] _pSign, out X509Certificate2 _pCert, ref string _sError, bool _fVerifyOnlySign = true, X509RevocationMode _pRevMode = X509RevocationMode.Online, X509RevocationFlag _pRevFlag = X509RevocationFlag.ExcludeRoot){ _pCert = null; IntPtr pHData = Marshal.AllocHGlobal(_arData.Length); GCHandle pCertContext = GCHandle.Alloc(IntPtr.Zero, GCHandleType.Pinned); try { Marshal.Copy(_arData, 0, pHData, _arData.Length); CRYPT_VERIFY_MESSAGE_PARA pVerParam = UCUtils.GetStdSignVerifyPar();
Der Einfachheit halber wurde der Prozess zum Bilden einer Struktur mit Parametern in eine separate Methode (GetStdSignVerifyPar) verschoben. Danach wird die Unterschrift selbst überprüft und der erste Unterzeichner extrahiert (für immer wäre es notwendig, alle zu extrahieren, aber eine Unterschrift mit mehreren Unterzeichnern ist immer noch exotisch).
Nach dem Extrahieren des Zertifikats des Unterzeichners konvertieren wir es in unsere Klasse und überprüfen es (falls in den Methodenparametern angegeben). Zur Überprüfung wird das Unterzeichnungsdatum des ersten Unterzeichners verwendet (siehe Abschnitt zum Extrahieren von Informationen aus der Unterschrift und Abschnitt zum Überprüfen des Zertifikats).
Signaturinformationen extrahieren
Kryptografische Systeme erfordern häufig eine gedruckte Darstellung der Signatur. In jedem Fall ist es anders, daher ist es besser, eine Klasse von Informationen über die Signatur zu erstellen, die Informationen in einer bequemen Form zur Verwendung enthält und mit ihrer Hilfe eine gedruckte Präsentation bereitstellt. In .Net gibt es eine solche Klasse - SignedCms. Das Analogon in Mono mit den Signaturen von CritiPro Pro funktioniert jedoch nicht. Erstens enthält es den versiegelten Modifikator und drittens sind fast alle Eigenschaften schreibgeschützt, sodass Sie Ihr eigenes Analogon erstellen müssen.
Die Unterschrift selbst enthält zwei Hauptelemente - eine Liste der Zertifikate und eine Liste der Unterzeichner. Die Liste der Zertifikate kann leer sein oder alle zu überprüfenden Zertifikate enthalten, einschließlich vollständiger Ketten. Die Liste der Unterzeichner gibt die Anzahl der tatsächlichen Unterschriften an. Die Kommunikation zwischen ihnen erfolgt über die Seriennummer und den Verlag (Emittent). Theoretisch kann es in einer Signatur zwei Zertifikate von verschiedenen Herausgebern mit derselben Seriennummer geben, in der Praxis kann dies jedoch vernachlässigt und nur anhand der Seriennummer durchsucht werden.
Das Lesen der Signatur lautet wie folgt:
Signaturinformationen extrahieren public int Decode(byte[] _arSign, ref string _sError) { IntPtr hMsg = IntPtr.Zero;
Die Signatur wird in mehreren Schritten analysiert. Zuerst wird die Nachrichtenstruktur (CryptMsgOpenToDecode) gebildet und anschließend werden echte Signaturdaten (CryptMsgUpdate) eingegeben. Es bleibt zu überprüfen, ob dies eine echte Signatur ist, und zuerst eine Liste der Zertifikate und dann eine Liste der Unterzeichner zu erhalten. Die Liste der Zertifikate wird nacheinander abgerufen:
Eine Liste der Zertifikate erhalten internal static X509Certificate2Collection GetSignCertificates(IntPtr _hMsg) { X509Certificate2Collection certificates = new X509Certificate2Collection(); uint iCnt = GetCryptMsgParam<uint>(_hMsg, UCConsts.CMSG_CERT_COUNT_PARAM); for (int i = 0; i < iCnt; i++) { IntPtr hInfo = IntPtr.Zero; IntPtr hCert = IntPtr.Zero; try { uint iLen = 0; if (!GetCryptMsgParam(_hMsg, UCConsts.CMSG_CERT_PARAM, out hInfo, out iLen)) continue; hCert = UCryptoAPI.CertCreateCertificateContext(UCConsts.PKCS_7_OR_X509_ASN_ENCODING, hInfo, iLen); if (hCert != IntPtr.Zero) { certificates.Add(new ISDP_X509Cert(hCert)); hCert = IntPtr.Zero; } } finally { if (hInfo != IntPtr.Zero) Marshal.FreeHGlobal(hInfo); if (hInfo != IntPtr.Zero) Marshal.FreeHGlobal(hCert); } } return certificates; }
Zuerst wird die Anzahl der Zertifikate aus dem Parameter CMSG_CERT_COUNT_PARAM bestimmt, und dann werden Informationen zu jedem Zertifikat nacheinander abgerufen. Der Prozess des Erstellens des Kontexts des Zertifikats und auf der Grundlage des Zertifikats selbst schließt den Erstellungsprozess ab.
Das Abrufen von Unterzeichnerdaten ist schwieriger. Sie enthalten eine Angabe des Zertifikats und eine Liste der Signaturparameter (z. B. das Datum der Signatur). Der Datenextraktionsprozess ist wie folgt:
Unterzeichnerinformationen abrufen public int Decode(IntPtr _hMsg, int _iIndex, ISDPSignedCms _pSignedCms, ref string _sError) {
, CMSG_SIGNER_INFO. . , .
, — ( , ).
internal static CryptographicAttributeObjectCollection ReadCryptoAttrsCollection(CRYPT_ATTRIBUTES _pAttrs) { CryptographicAttributeObjectCollection pRes = new CryptographicAttributeObjectCollection(); for (int i = 0; i < _pAttrs.cAttr; i++) { IntPtr hAttr = new IntPtr((long)_pAttrs.rgAttr + (i * Marshal.SizeOf(typeof(CRYPT_ATTRIBUTE)))); CRYPT_ATTRIBUTE pAttr = (CRYPT_ATTRIBUTE) Marshal.PtrToStructure(hAttr, typeof(CRYPT_ATTRIBUTE)); CryptographicAttributeObject pAttrInfo = new CryptographicAttributeObject(new Oid(pAttr.pszObjId), GetAsnEncodedDataCollection(pAttr)); pRes.Add(pAttrInfo); } return pRes; }
Oid – ( ASN.1). :
internal static Pkcs9AttributeObject Pkcs9AttributeFromOID(string _sName) { switch (_sName) { case UCConsts.S_SIGN_DATE_OID : return new Pkcs9SigningTime();
Pkcs9AttributeObject. , mono . Mono .
— — SignedCms, .
, , . (, , ).
public static int EncryptDataCP(byte[] _arInput, X509Certificate2 _pCert, out byte[] _arRes, ref string _sError) { _arRes = new byte[0]; try {
— , . , , .
, , .
. , ( ). :
internal static int GetEncodeAlgoritmOID(IntPtr _hCertHandle, out string _sOID, ref string _sError) { bool fNeedRelease = false; _sOID = ""; uint iKeySpec = 0; IntPtr hCrypto = IntPtr.Zero; try {
. ( , , , .), . (UCConsts.CRYPT_ENCRYPT_ALG_OID_GROUP_ID). — .
( ).
, , . . — , :
public static int DecryptDataCP(byte[] _arInput, out X509Certificate2 _pCert, out byte[] _arRes, ref string _sError) { _arRes = new byte[0]; _pCert = null; IntPtr hSysStore = UCryptoAPI.CertOpenSystemStore(IntPtr.Zero, UCConsts.AR_CRYPTO_STORE_NAME[(int)StoreName.My]); GCHandle GC = GCHandle.Alloc(hSysStore, GCHandleType.Pinned); IntPtr hOutCertL = IntPtr.Zero; IntPtr hOutCert = IntPtr.Zero; try {
, . , ( Linux ).
, , , , . , . :
- ( , , . .);
- — ;
- — ;
- , , (CRL);
, .
Wie bereits aus der Einführung hervorgeht, ist die Überprüfung des Zertifikats auf Gültigkeit eine der schwierigsten Aufgaben. Aus diesem Grund verfügt die Bibliothek über viele Methoden, um jedes Element einzeln zu implementieren. Wenden wir uns daher zur Vereinfachung den .Net-Quellen für die X509Certificate2.Verify () -Methode zu und nehmen Sie sie als Grundlage.
Die Überprüfung besteht aus zwei Schritten:- Bilden Sie eine Kette von Zertifikaten bis zur Wurzel.
- Überprüfen Sie jedes der darin enthaltenen Zertifikate (auf Widerruf, Zeit usw.).
Diese Überprüfung muss vor dem Signieren und Verschlüsseln am aktuellen Datum und zum Zeitpunkt der Überprüfung der Signatur am Datum des Signierens durchgeführt werden. Die Überprüfungsmethode selbst ist klein:
Zertifikatsüberprüfung internal static int VerifyCertificate (IntPtr _hCert, X509RevocationMode _iRevMode, X509RevocationFlag _iRevFlag, DateTime _rOnDate, TimeSpan _iCTLTimeout, IntPtr _hPolicy, ref string _sError) { if (_hCert == IntPtr.Zero) { _sError = UCConsts.S_CRYPTO_CERT_CHECK_ERR; return UConsts.E_NO_CERTIFICATE; } CERT_CHAIN_POLICY_PARA pPolicyParam = new CERT_CHAIN_POLICY_PARA(Marshal.SizeOf(typeof(CERT_CHAIN_POLICY_PARA))); CERT_CHAIN_POLICY_STATUS pPolicyStatus = new CERT_CHAIN_POLICY_STATUS(Marshal.SizeOf(typeof(CERT_CHAIN_POLICY_STATUS)));
Zuerst wird eine Kette mit der BuildChain-Methode gebildet und dann überprüft. Während der Kettenbildung werden die Struktur der Parameter, das Datum der Überprüfung und die Kontrollflags gebildet:
internal static int BuildChain (IntPtr _hChainEngine, IntPtr _hCert, X509RevocationMode _iRevMode, X509RevocationFlag _iRevFlag, DateTime _rOnDate, TimeSpan _rCTLTimeOut, ref IntPtr _hChain, ref string _sError) {
, Microsoft. hCertPolicy hAppPolicy OID-, , . , , .
(, ).
MapRevocationFlags — .Net — uint .
Fazit
:
- 10 ;
- ;
- byte[] {1, 2, 3, 4, 5};
- ;
- ;
- byte[] {1, 2, 3, 4, 5};
- ;
Windows Linux 1-, 10- 50- , Linux . Linux - - ( , ), «» . (deadlock-) ( «Access Violation»).
UCryptoAPI . fpCPSection object :
private static object fpCPSection = new object(); internal static bool CryptMsgClose(IntPtr _hCryptMsg) { lock (pCPSection) { if (fIsLinux) return LCryptoAPI.CryptMsgClose(_hCryptMsg); else return WCryptoAPI.CryptMsgClose(_hCryptMsg); } } public static object pCPSection { get { return fpCPSection;} }
, Linux- .
mono Issuer Subject . , , mono X500DistinguishedName . , mono ( ), (impl.issuerName impl.subjectName). (Reflection) X500DistinguishedName, CERT_CONTEXT .
Referenzen
- CAPILite
- c #
- .Net:
- CAPIBase
- X509Certificate2
- SignedCMS
- SignerInfo
- mono:
- X509Certificate2
- X509CertificateImplBtls