ikvmnet / ikvm

A Java Virtual Machine and Bytecode-to-IL Converter for .NET
Other
1.27k stars 120 forks source link

IKVM compile error on Redhat / Rocky linux 9 (Legacy Digest Algorithm) #600

Open jweibull opened 1 month ago

jweibull commented 1 month ago

I am trying to use IKVM and IKVM.Maven on a project that will be compiled on a Redhat Enterprise Linux 9.x.

While trying to build the DLLs from Apache POI, it throws several warnings and an error:

dotnet build IkvmPOI.csproj
MSBuild version 17.8.5+b5265ef37 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :  [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning : *** COMPILER ERROR *** [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :  [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning : IKVM.Tools.Importer, Version=8.10.2.0, Culture=neutral, PublicKeyToken=13235d27fcbfff58 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning : /root/.nuget/packages/ikvm.msbuild.tools.runtime.linux-x64/8.10.2/ikvmc/net8.0/linux-x64/ [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning : 8.0.8 64-bit [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :  [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning : Interop+Crypto+OpenSslCryptographicException: error:03000098:digital envelope routines::invalid digest [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at System.Security.Cryptography.RSAOpenSsl.SignHash(Byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Reflection.Writer.ModuleWriter.GetSignature(StrongNameKeyPair keyPair, IEnumerable`1 blobs, Int32 strongNameSignatureSize) in /_/src/IKVM.Reflection/Writer/ModuleWriter.cs:line 572 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at System.Reflection.PortableExecutable.PEBuilder.Sign(BlobBuilder peImage, Blob strongNameSignatureFixup, Func`2 signatureProvider) [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Reflection.Writer.ModuleWriter.WriteModuleImpl(StrongNameKeyPair keyPair, Byte[] publicKey, ModuleBuilder module, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ModuleResourceSectionBuilder nativeResources, MethodInfo entryPoint, String peFileName, Stream peStream, String pdbFileName, Stream pdbStream) in /_/src/IKVM.Reflection/Writer/ModuleWriter.cs:line 263 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Reflection.Writer.ModuleWriter.WriteModule(StrongNameKeyPair keyPair, Byte[] publicKey, ModuleBuilder module, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ModuleResourceSectionBuilder nativeResources, MethodInfo entryPoint, String peFileName, Stream peStream, String pdbFileName, Stream pdbStream) in /_/src/IKVM.Reflection/Writer/ModuleWriter.cs:line 116 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Reflection.Emit.AssemblyBuilder.SaveImpl(String assemblyFileName, Stream peStream, Stream pdbStream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) in /_/src/IKVM.Reflection/Emit/AssemblyBuilder.cs:line 516 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Tools.Importer.CompilerClassLoader.Save() in /_/src/IKVM.Tools.Importer/CompilerClassLoader.cs:line 642 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Tools.Importer.CompilerClassLoader.Compile(RuntimeContext context, StaticCompiler compiler, String runtimeAssembly, List`1 optionsList) in /_/src/IKVM.Tools.Importer/CompilerClassLoader.cs:line 2566 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Tools.Importer.IkvmImporterInternal.Compile(String[] args) in /_/src/IKVM.Tools.Importer/IkvmImporterInternal.cs:line 196 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): warning :    at IKVM.Tools.Importer.IkvmImporterInternal.Execute(String[] args) in /_/src/IKVM.Tools.Importer/IkvmImporterInternal.cs:line 113 [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]
/root/.nuget/packages/ikvm/8.10.2/buildTransitive/IKVM.IkvmReference.Tasks.targets(31,9): error MSB4181: The "IkvmCompiler" task returned false but did not log an error. [/home/IkvmPOI/IkvmPOI/IkvmPOI.csproj]

Build FAILED.

Investigating we discovered that legacy digest algorithms are defaulted to SHA1 which are not supported on Openssl 3. The obvious solution is installing compat-ssl1.1 and configuring for legacy support (tested). But that is not an option for us, since we dont own the deploy server and cannot alter ssl configurations or install ssl1.1.

We found code that could allow us to choose the hash algorithm inside IKVM.Reflection, but we failed to find a way to configure it through our project file or even through assembly annotations.

Is there a way to configure? Or, would it be possible to add support for configuring the Hash to SHA256, for example?

Thank you in advance!!!

wasabii commented 1 month ago

Interesting. Seems to work fine for me on Ubuntu. Going to need to think hard about this one.

jweibull commented 1 month ago

It works on Ubuntu because legacy OpenSSL support is enabled (I tried it myself). If you try it on a Rocky Distro (free) that mimics Redhat you'll get the error. Redhat abandoned OpenSSL 1.1 and defaulted to strict OpenSSL 3 for security reasons.

It works if you re-enable support for legacy SSL methods like SHA1, which I can't.

wasabii commented 1 month ago

So this is a complicated topic. Let me lay down some basics: there is no way I can see that I'm going to allow (in a supported fashion) people to alter the strong names of IKVM.Maven generated assemblies. IKVM.Maven depends very much on generated assemblies being consistent between disparete developers and machines. If two library authors depend on a Maven Package and both user publish their .NET library to NuGet, then both of their produced assemblies must depend on the same generated Maven assemblies. And strong names are part of that. We cannot have User 1's assembly reference be different than User 2's. So IKVM.Maven provides only a deterministic process for configuring IKVM. Can't adjust assembly name, version, or anything else.

Second, strong names are a legacy feature. There are three reasons we use them at all, and they all relate to limitations of Framework: a) so users who are forced to strong-name their assemblies can access our assemblies (Framework requires this). b) so users of Framework could potentially register their assemblies in the GAC and c) so we can pre-add a Friend assembly for dynamic byte code injection with package-access (Framework requires that to be signed). Microsoft long ago acknowledged they are not a security relevent feature. Our private key is even published in the open, like most open source libraries.

So this is kind of a silly situtation. We either need to wholly embrace SHA256 (Enhanced Strong Naming) so this can work on a machine with SHA1 disabled: where the usage of SHA1 isn't even security relevent. Or we need to write/use a managed implementation of SHA1. The first option is pretty invasive at this point: we have IKVM.Maven.Sdk users in the wild now, with NuGet packages published that depend on SHA1 hashed Maven assemblies. So, if we changed our signing algorithm, we would be breaking those packages.

And for what? If strong names were a security feature, I'd be on board with that. But they aren't. They're just some stupid thing we only do now for backwards compatibility.

Using a managed implementation of SHA1 on Core seems like the only option we could viably do at this point.

wasabii commented 1 month ago

Question: are you trying to cross compile Framework assemblies on your RedHat box? Or is this just Core?

Because I just looked and we are strong naming Core assemblies to. And for the life of me I can't remember why. Because we probably shouldn't be......

wasabii commented 1 month ago

So I think this is the reason I opted to strong name Core assemblies:

Strong naming has no benefits on .NET Core/5+. C# compiler produces CS8002 warning for strong-named assemblies referencing non-strong named assemblies. It is fine to suppress this warning for libraries that target .NET Core/5+ only.

To make the warning go away for everybody, and because it didn't matter.

jweibull commented 1 month ago

Explaining a bit more about our pipeline:

We were using IKVM together with IKVM.Maven in a .Net Core 8.0 exclusive project to access Apache POI functionality. The project restores ok, but during build time, when it compiles the JAR files from maven references it throws the errors due to unsupported legacy digest algorithms.

It worked in Ubuntu and Windows with no problem, but we had to deploy in Redhat and got unexpected errors with the digest algorithm. We were able to mock the problem using Rocky distro, which is free.

Although I totally agree, strong naming is not a real security issue, we are still unable to meddle with the machine configuration. That's why I tried asking if it was possible to configure the digest algorithm and I understand and agree with your arguments.

Even if linux allows, in normal circuntances, installing OpenSSL1.1 packages and configuring for legacy support, It would prevent IKVM from working out of the box. Others might be in a similar situation where the corporate server can't be reconfigured. In that sense, the embedded managed hashing library might be needed sooner or later. Newer linux distros are abandoning "out of the box" support to OpenSSL 1.1.

Anyway, now we are certain of the situation. Unfortunately we will have to abandon using IKVM while the corporate distro doesn't allow it's use. Many thanks for the fast replies.

hez2010 commented 3 weeks ago

If two library authors depend on a Maven Package and both user publish their .NET library to NuGet, then both of their produced assemblies must depend on the same generated Maven assemblies. And strong names are part of that. We cannot have User 1's assembly reference be different than User 2's. So IKVM.Maven provides only a deterministic process for configuring IKVM. Can't adjust assembly name, version, or anything else.

You can drop the strong name. On .NET Core/.NET 5+ all strong names will be simply ignored while resolving assembly. Assemblies with same name and version, but different strong name will be treated as the same.

wasabii commented 3 weeks ago

If two library authors depend on a Maven Package and both user publish their .NET library to NuGet, then both of their produced assemblies must depend on the same generated Maven assemblies. And strong names are part of that. We cannot have User 1's assembly reference be different than User 2's. So IKVM.Maven provides only a deterministic process for configuring IKVM. Can't adjust assembly name, version, or anything else.

You can drop the strong name. On .NET Core/.NET 5+ all strong names will be simply ignored while resolving assembly.

We support Framework. But also there's an annoying warning on Core.

hez2010 commented 3 weeks ago

Then it's the responsibility of the library author to choose whether to upload the library signed with strong name along with the unsigned one or not to the nuget: https://www.nuget.org/packages?q=StrongName

I think you can simply expose a msbuild property to suppress it.

wasabii commented 3 weeks ago

Then it's the responsibility of the library author to choose whether to upload the library signed with strong name along with the unsigned one or not to the nuget: https://www.nuget.org/packages?q=StrongName

I think you can simply expose a msbuild property to suppress it.

We are the library author? I think IKVM.Maven is more complicated than you're realizing here.

hez2010 commented 3 weeks ago

Then how about switching to use BouncyCastle.Cryptography, which is implemented in managed code so that it doesn't have the issue with openssl?

wasabii commented 3 weeks ago

I am probably going to switch to a managed implementation, yes.