Bazsay
April 9, 2024, 8:24am
1
Hi!
We are evaluating the library, and we are happy with what we are seeing, but after updating to .NET 8, we see the following error:
CS0433: The type ‘InvalidCipherTextException’ exists in both ‘BouncyCastle.Crypto’ and ‘BoundyCastle.Cryptography’
We need to use BouncyCastle in our solution, we are using the BouncyCastle.Crypto package, as the Shared.BouncyCastle is deprecated.
But as we checked on the Nuget page, the .NETStandard 2.0 version of GemBox.Document is still using the Portable.BouncyCastle.
Is there a plan for changing it to the BouncyCastle.Cryptography ?
I saw a post for the GemBox.Email , but this package has a separate .Net Framework 3.5 edition.
Hi Batsay,
We would like to move to BouncyCastle.Cryptography for the .NET Standard 2.0 version (we already moved to it for our .NET Framework 4.6.2 and .NET 6.0 Windows versions), but unfortunately, we can’t until they fix this bug that we reported a year ago:
opened 09:46AM - 04 Apr 23 UTC
### Description
When using a project or a package that targets `netstandard2.0`… , references [BouncyCastle.Cryptography](https://www.nuget.org/packages/BouncyCastle.Cryptography) package, and implements some interfaces from BouncyCastle.Cryptography, such as [ISigner](https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/ISigner.cs), in a project that targets `net6.0`, [System.TypeLoadException](https://learn.microsoft.com/en-us/dotnet/api/system.typeloadexception) is thrown with the message: _Method does not have an implementation_.
### Reproduction Steps
Visual Studio solution that reproduces the issue: [BouncyCastleCryptographyInterfaceIssue.zip](https://github.com/bcgit/bc-csharp/files/11147133/BouncyCastleCryptographyInterfaceIssue.zip)
ClassLibrary1.csproj:
```
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.1.1" />
</ItemGroup>
</Project>
```
ClassLibrary1.MySigner.cs:
```
using Org.BouncyCastle.Crypto;
namespace ClassLibrary1
{
public class MySigner : ISigner
{
public string AlgorithmName => "MyAlgorithmName";
public void BlockUpdate(byte[] input, int inOff, int inLen)
{
}
public byte[] GenerateSignature()
{
return new byte[0];
}
public int GetMaxSignatureSize()
{
return 0;
}
public void Init(bool forSigning, ICipherParameters parameters)
{
}
public void Reset()
{
}
public void Update(byte input)
{
}
public bool VerifySignature(byte[] signature)
{
return false;
}
}
}
```
ConsoleApp1.csproj:
```
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
</ItemGroup>
</Project>
```
ConsoleApp1.Program.cs:
```
using System;
namespace ConsoleApp1
{
internal class Program
{
static void Main()
{
var mySignerAlgorithmName = GetMySignerAlgorithmName();
Console.WriteLine(mySignerAlgorithmName);
}
private static string GetMySignerAlgorithmName()
{
return new ClassLibrary1.MySigner().AlgorithmName;
}
}
}
```
### Expected behavior
`MyAlgorithmName` is output to the Console.
### Actual behavior
An exception is thrown:
```
System.TypeLoadException
HResult=0x80131522
Message=Method 'BlockUpdate' in type 'ClassLibrary1.MySigner' from assembly 'ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
Source=ConsoleApp1
StackTrace:
at ConsoleApp1.Program.GetMySignerAlgorithmName() in C:\Users\Stipo\Documents\Visual Studio 2022\Projects\BouncyCastleCryptographyInterfaceIssue\ConsoleApp1\Program.cs:line 17
at ConsoleApp1.Program.Main() in C:\Users\Stipo\Documents\Visual Studio 2022\Projects\BouncyCastleCryptographyInterfaceIssue\ConsoleApp1\Program.cs:line 9
```
### Regression?
Probably yes, after the member [Org.BouncyCastle.Crypto.ISigner.BlockUpdate(ReadOnlySpan<byte> input)](https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/ISigner.cs#L25) was added and after other conditional compilation members were added to other interfaces.
### Known Workarounds
- A project or package that references [BouncyCastle.Cryptography](https://www.nuget.org/packages/BouncyCastle.Cryptography) package should target both `netstandard2.0` and `net6.0`, but that is not possible if a package is coming from a third party.
- Force usage of BouncyCastle.Cryptography.dll for `netstandard2.0` by adding the following to the ConsoleApp1.csproj:
```
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.1.1" ExcludeAssets="Compile" GeneratePathProperty="true" />
<Reference Include="BouncyCastle.Cryptography">
<HintPath>$(PkgBouncyCastle_Cryptography)\lib\netstandard2.0\BouncyCastle.Cryptography.dll</HintPath>
</Reference>
</ItemGroup>
```
But this is ugly because users of a third party package must take care of its BouncyCastle.Cryptography package dependency although they are not using the BouncyCastle.Cryptography package directly in their code.
### Other information
The issue is happening because there are several interfaces in [BouncyCastle.Cryptography](https://www.nuget.org/packages/BouncyCastle.Cryptography) package (such as [Org.BouncyCastle.Crypto.ISigner](https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/ISigner.cs#L25) that add a member conditionally when targeting `net6.0`, thus changing the interface contract from the compatible `netstandard2.0` targeted framework.
The [Change rules for compatibility](https://learn.microsoft.com/en-us/dotnet/core/compatibility/library-change-rules) state:
> ❌ **DISALLOWED: Adding a member to an interface**
> If you [provide an implementation](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/interface-implementation/default-interface-methods-versions), adding a new member to an existing interface won't necessarily result in compile failures in downstream assemblies. However, not all languages support default interface members (DIMs). Also, in some scenarios, the runtime can't decide which default interface member to invoke. For these reasons, adding a member to an existing interface is considered a breaking change.
The solution is to either remove the problematic interface members (conditionally added ones, such as [Org.BouncyCastle.Crypto.ISigner.BlockUpdate(ReadOnlySpan<byte> input)](https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/ISigner.cs#L25)) (**this would be a breaking change of BouncyCastle.Cryptography API**) or provide a default implementation for them as explained in the [Tutorial: Update interfaces with default interface methods](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/interface-implementation/default-interface-methods-versions).
For example, the fix for the [Org.BouncyCastle.Crypto.ISigner.BlockUpdate(ReadOnlySpan<byte> input)](https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/ISigner.cs#L25) would be:
```
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
/// <summary>Update the signer with a span of bytes.</summary>
/// <param name="input">the span containing the data.</param>
public void BlockUpdate(ReadOnlySpan<byte> input) => this.BlockUpdate(input.ToArray(), 0, input.Length);
#endif
```
Anyway, as a workaround for now, can you please try using the following in your CSPROJ file:
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" ExcludeAssets="Compile" GeneratePathProperty="true" />
<Reference Include="BouncyCastle.Crypto">
<HintPath>$(PkgPortable_BouncyCastle)\lib\netstandard2.0\BouncyCastle.Crypto.dll</HintPath>
<Aliases>PortableBouncyCastle</Aliases>
</Reference>
</ItemGroup>
Does this solve your issue?
Regards,
Mario