From 1c99e5d92a32eedb8e235b1b9089555dedbaf695 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Thu, 10 Oct 2024 07:31:29 +0300 Subject: [PATCH] Fix Java/C# memory leaks and handle X509Cert as native type where possible (#631) IB-8235 Signed-off-by: Raul Metsma --- examples/DigiDocCSharp/DigiDocCSharp.csproj | 5 +- examples/DigiDocCSharp/Program.cs | 6 +- .../ee/ria/libdigidocpp/libdigidocpp.java | 5 +- libdigidocpp.i | 203 +++++++----------- src/CMakeLists.txt | 2 +- 5 files changed, 85 insertions(+), 136 deletions(-) diff --git a/examples/DigiDocCSharp/DigiDocCSharp.csproj b/examples/DigiDocCSharp/DigiDocCSharp.csproj index 8fb78a012..95123b394 100644 --- a/examples/DigiDocCSharp/DigiDocCSharp.csproj +++ b/examples/DigiDocCSharp/DigiDocCSharp.csproj @@ -2,9 +2,8 @@ net472 Exe - true - 0.4.0.0 - 0.4.0.0 + 0.5.0.0 + 0.5.0.0 Copyright © 2015 diff --git a/examples/DigiDocCSharp/Program.cs b/examples/DigiDocCSharp/Program.cs index ed3accb6b..c5f25cfa4 100644 --- a/examples/DigiDocCSharp/Program.cs +++ b/examples/DigiDocCSharp/Program.cs @@ -154,8 +154,7 @@ private static void Websign(string[] args) b.addDataFile(args[i], "application/octet-stream"); } - X509Certificate cert = new X509Certificate(); - cert.Import(args[args.Length - 2]); + var cert = new X509Certificate(args[args.Length - 2]); Signature c = b.prepareWebSignature(cert.Export(X509ContentType.Cert), "time-stamp"); Console.WriteLine("Signature method: " + c.signatureMethod()); Console.WriteLine("Digest to sign: " + BitConverter.ToString(c.dataToSign()).Replace("-", string.Empty)); @@ -207,7 +206,8 @@ private static void Verify(string file) Console.WriteLine(); Console.WriteLine("Time: " + s.trustedSigningTime()); - Console.WriteLine("Cert: " + new X509Certificate2(s.signingCertificateDer()).Subject); + Console.WriteLine("Cert: " + s.signingCertificate().Subject); + Console.WriteLine("TimeStamp: " + s.TimeStampCertificate().Subject); s.validate(); Console.WriteLine("Signature is valid"); diff --git a/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java b/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java index bbe59f2bc..bf6050226 100644 --- a/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java +++ b/examples/java/src/main/java/ee/ria/libdigidocpp/libdigidocpp.java @@ -149,7 +149,8 @@ static void verify(String file) { System.out.println(); System.out.println("Time: " + signature.trustedSigningTime()); - System.out.println("Cert: " + toX509(signature.signingCertificateDer()).getSubjectDN().toString()); + System.out.println("Cert: " + signature.signingCertificate().getSubjectDN().toString()); + System.out.println("TimeStamp Cert: " + signature.TimeStampCertificate().getSubjectDN().toString()); try { @@ -171,7 +172,7 @@ static void verify(String file) { } static void version() { - System.out.println("DigiDocJAVA 0.3 libdigidocpp " + digidoc.version()); + System.out.println("DigiDocJAVA 0.4 libdigidocpp " + digidoc.version()); } static X509Certificate toX509(byte[] der) throws CertificateException { diff --git a/libdigidocpp.i b/libdigidocpp.i index f64ba3ee0..ef2e5884f 100644 --- a/libdigidocpp.i +++ b/libdigidocpp.i @@ -60,6 +60,9 @@ extern "C" SWIGEXPORT int SWIGSTDCALL ByteVector_size(void *ptr) { return static_cast*>(ptr)->size(); } + SWIGEXPORT void SWIGSTDCALL ByteVector_free(void *ptr) { + delete static_cast*>(ptr); + } SWIGEXPORT void* SWIGSTDCALL ByteVector_to(unsigned char *data, int size) { return new std::vector(data, data + size); } @@ -72,112 +75,93 @@ extern "C" public static extern global::System.IntPtr ByteVector_data(global::System.IntPtr data); [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="ByteVector_size")] public static extern int ByteVector_size(global::System.IntPtr data); + [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="ByteVector_free")] + public static extern void ByteVector_free(global::System.IntPtr data); [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="ByteVector_to")] public static extern global::System.IntPtr ByteVector_to( - [global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPArray)]byte[] data, int size); - - public class UTF8Marshaler : global::System.Runtime.InteropServices.ICustomMarshaler { - static UTF8Marshaler static_instance = new UTF8Marshaler(); - - public global::System.IntPtr MarshalManagedToNative(object managedObj) { - if (managedObj == null) - return global::System.IntPtr.Zero; - if (!(managedObj is string)) - throw new global::System.Runtime.InteropServices.MarshalDirectiveException( - "UTF8Marshaler must be used on a string."); - - // not null terminated - byte[] strbuf = global::System.Text.Encoding.UTF8.GetBytes((string)managedObj); - global::System.IntPtr buffer = global::System.Runtime.InteropServices.Marshal.AllocHGlobal(strbuf.Length + 1); - global::System.Runtime.InteropServices.Marshal.Copy(strbuf, 0, buffer, strbuf.Length); - - // write the terminating null - global::System.Runtime.InteropServices.Marshal.WriteByte(buffer + strbuf.Length, 0); - return buffer; - } - - public unsafe object MarshalNativeToManaged(global::System.IntPtr pNativeData) { - byte* walk = (byte*)pNativeData; - - // find the end of the string - while (*walk != 0) { - walk++; - } - int length = (int)(walk - (byte*)pNativeData); - - // should not be null terminated - byte[] strbuf = new byte[length]; - // skip the trailing null - global::System.Runtime.InteropServices.Marshal.Copy((global::System.IntPtr)pNativeData, strbuf, 0, length); - return global::System.Text.Encoding.UTF8.GetString(strbuf); - } - - public void CleanUpNativeData(global::System.IntPtr pNativeData) { - global::System.Runtime.InteropServices.Marshal.FreeHGlobal(pNativeData); - } - - public void CleanUpManagedData(object managedObj) { - } - - public int GetNativeDataSize() { - return -1; - } - - public static global::System.Runtime.InteropServices.ICustomMarshaler GetInstance(string cookie) { - return static_instance; - } - } + [global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPArray)]byte[] data, int size); %} #ifdef SWIGJAVA -%typemap(in) std::vector %{ - jbyte *$input_ptr = jenv->GetByteArrayElements($input, NULL); - jsize $input_size = jenv->GetArrayLength($input); - std::vector $1_data($input_ptr, $input_ptr+$input_size); - $1 = &$1_data; - jenv->ReleaseByteArrayElements($input, $input_ptr, JNI_ABORT); -%} -%typemap(out) std::vector %{ - jresult = jenv->NewByteArray((&result)->size()); - jenv->SetByteArrayRegion(jresult, 0, (&result)->size(), (const jbyte*)(&result)->data()); -%} -%typemap(jtype) std::vector "byte[]" +%fragment("SWIG_VectorUnsignedCharToJavaArray", "header") { +static jbyteArray SWIG_VectorUnsignedCharToJavaArray(JNIEnv *jenv, const std::vector &data) { + jbyteArray jresult = JCALL1(NewByteArray, jenv, data.size()); + if (!jresult) + return nullptr; + JCALL4(SetByteArrayRegion, jenv, jresult, 0, data.size(), (const jbyte*)data.data()); + return jresult; +}} +%fragment("SWIG_JavaArrayToVectorUnsignedChar", "header") { +static std::vector* SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jenv, jbyteArray data) { + std::vector *result = new std::vector(JCALL1(GetArrayLength, jenv, data)); + JCALL4(GetByteArrayRegion, jenv, data, 0, result->size(), (jbyte*)result->data()); + return result; +}} +%typemap(in, fragment="SWIG_JavaArrayToVectorUnsignedChar") std::vector +%{ $1 = SWIG_JavaArrayToVectorUnsignedChar(jenv, $input); %} +%typemap(out, fragment="SWIG_VectorUnsignedCharToJavaArray") std::vector, digidoc::X509Cert +%{ $result = SWIG_VectorUnsignedCharToJavaArray(jenv, $1); %} +%typemap(jtype) std::vector, digidoc::X509Cert "byte[]" %typemap(jstype) std::vector "byte[]" -%typemap(jni) std::vector "jbyteArray" -%typemap(javain) std::vector "$javainput" +%typemap(jstype) digidoc::X509Cert "java.security.cert.X509Certificate" +%typemap(jni) std::vector, digidoc::X509Cert "jbyteArray" +%typemap(javain) std::vector, digidoc::X509Cert "$javainput" %typemap(javaout) std::vector { return $jnicall; } +%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert { + byte[] der = $jnicall; + java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X509"); + try (java.io.ByteArrayInputStream is = new java.io.ByteArrayInputStream(der)) { + return (java.security.cert.X509Certificate) cf.generateCertificate(is); + } + } + #elif defined(SWIGCSHARP) %typemap(cstype) std::vector "byte[]" -%typemap(csin, - pre= " global::System.IntPtr cPtr$csinput = digidocPINVOKE.ByteVector_to($csinput, $csinput.Length); - global::System.Runtime.InteropServices.HandleRef handleRef$csinput = new global::System.Runtime.InteropServices.HandleRef(this, cPtr$csinput);" +%typemap(cstype) digidoc::X509Cert "System.Security.Cryptography.X509Certificates.X509Certificate2" +%typemap(csin, pre= " global::System.IntPtr cPtr$csinput = digidocPINVOKE.ByteVector_to($csinput, $csinput.Length); + var handleRef$csinput = new global::System.Runtime.InteropServices.HandleRef(this, cPtr$csinput);" ) std::vector "handleRef$csinput" %typemap(csout, excode=SWIGEXCODE) std::vector { - global::System.IntPtr data = $imcall;$excode - byte[] result = new byte[$modulePINVOKE.ByteVector_size(data)]; - global::System.Runtime.InteropServices.Marshal.Copy($modulePINVOKE.ByteVector_data(data), result, 0, result.Length); - return result; -} -#elif defined(SWIGPYTHON) - %typemap(in) std::vector %{ - if (PyBytes_Check($input)) { - const char *data = PyBytes_AsString($input); - $1 = new std::vector(data, data + PyBytes_Size($input)); - } else if (PyString_Check($input)) { - const char *data = PyString_AsString($input); - $1 = new std::vector(data, data + PyString_Size($input)); - } else { - PyErr_SetString(PyExc_TypeError, "not a bytes"); - SWIG_fail; + global::System.IntPtr cPtr = $imcall;$excode + byte[] result = new byte[$modulePINVOKE.ByteVector_size(cPtr)]; + global::System.Runtime.InteropServices.Marshal.Copy($modulePINVOKE.ByteVector_data(cPtr), result, 0, result.Length); + $modulePINVOKE.ByteVector_free(cPtr); + return result; + } +%typemap(csout, excode=SWIGEXCODE) digidoc::X509Cert { + global::System.IntPtr cPtr = $imcall;$excode + byte[] der = new byte[$modulePINVOKE.ByteVector_size(cPtr)]; + global::System.Runtime.InteropServices.Marshal.Copy($modulePINVOKE.ByteVector_data(cPtr), der, 0, der.Length); + $modulePINVOKE.ByteVector_free(cPtr); + return new System.Security.Cryptography.X509Certificates.X509Certificate2(der); } - %} +%typemap(out) std::vector %{ $result = new std::vector(std::move($1)); %} +%typemap(out) digidoc::X509Cert %{ $result = new std::vector($1); %} + +#elif defined(SWIGPYTHON) +%typemap(in) std::vector %{ + if (PyBytes_Check($input)) { + const char *data = PyBytes_AsString($input); + $1 = new std::vector(data, data + PyBytes_Size($input)); + } else if (PyString_Check($input)) { + const char *data = PyString_AsString($input); + $1 = new std::vector(data, data + PyString_Size($input)); + } else { + PyErr_SetString(PyExc_TypeError, "not a bytes"); + SWIG_fail; + } +%} %typemap(out) std::vector %{ $result = PyBytes_FromStringAndSize((const char*)(&result)->data(), (&result)->size()); %} +%typemap(out) digidoc::X509Cert { + std::vector temp = $1; + $result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size()); +} +#endif %typemap(freearg) std::vector %{ delete $1; %} -#endif %apply std::vector { std::vector const & }; %exception %{ @@ -208,11 +192,6 @@ extern "C" %ignore digidoc::ConfV2::verifyServiceCert; %ignore digidoc::ConfV4::verifyServiceCerts; %ignore digidoc::ConfV5::TSCerts; -%ignore digidoc::Signer::cert; -%ignore digidoc::Signature::signingCertificate; -%ignore digidoc::Signature::OCSPCertificate; -%ignore digidoc::Signature::TimeStampCertificate; -%ignore digidoc::Signature::ArchiveTimeStampCertificate; // hide stream methods, swig cannot generate usable wrappers %ignore digidoc::DataFile::saveAs(std::ostream &os) const; %ignore digidoc::Container::addAdESSignature(std::istream &signature); @@ -260,15 +239,10 @@ def transfer(self): %include "std_vector.i" %include "std_map.i" #ifdef SWIGCSHARP -namespace std { - %typemap(imtype, - inattributes="[global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]", - outattributes="[return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]") - string "string" - %typemap(imtype, - inattributes="[global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]", - outattributes="[return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]") const string & "string" -} +%typemap(imtype, + inattributes="[global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPUTF8Str)]", + outattributes="[return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPUTF8Str)]") + std::string, const std::string & "string" #endif // Handle DigiDoc Export declarations @@ -294,31 +268,6 @@ namespace std { %template(DataFiles) std::vector; %template(Signatures) std::vector; -// override X509Cert methods to return byte array -%extend digidoc::Signer { - std::vector cert() const - { - return $self->cert(); - } -} -%extend digidoc::Signature { - std::vector signingCertificateDer() const - { - return $self->signingCertificate(); - } - std::vector OCSPCertificateDer() const - { - return $self->OCSPCertificate(); - } - std::vector TimeStampCertificateDer() const - { - return $self->TimeStampCertificate(); - } - std::vector ArchiveTimeStampCertificateDer() const - { - return $self->ArchiveTimeStampCertificate(); - } -} %extend digidoc::Container { static digidoc::Container* open(const std::string &path, digidoc::ContainerOpenCB *cb) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c4363e0cb..01317a3d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -202,7 +202,7 @@ if(SWIG_FOUND) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/digidoc.py DESTINATION ${Python3_SITELIB}) endif() - set(CMAKE_SWIG_FLAGS -dllimport digidoc_csharp -namespace digidoc) + set(CMAKE_SWIG_FLAGS -namespace digidoc) set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/csharp) swig_add_library(digidoc_csharp LANGUAGE csharp SOURCES ../libdigidocpp.i) target_compile_definitions(digidoc_csharp PRIVATE TARGET_NAME="$")