What's new in .NET Core 3.0 (Preview 6)

This article describes what is new in .NET Core 3.0 (through preview 6). One of the biggest enhancements is support for Windows desktop applications (Windows only). By using the .NET Core 3.0 SDK component Windows Desktop, you can port your Windows Forms and Windows Presentation Foundation (WPF) applications. To be clear, the Windows Desktop component is only supported and included on Windows. For more information, see the Windows desktop section later in this article.

.NET Core 3.0 adds support for C# 8.0. It's highly recommended that you use the latest release of Visual Studio Preview, or Visual Studio Code with the OmniSharp extension.

Download and get started with .NET Core 3.0 Preview 6 right now on Windows, Mac, and Linux.

For more information about each preview release, see the following announcements:

.NET Core SDK Windows Installer

The MSI installer for Windows has changed starting with .NET Core 3.0. The SDK installers will now upgrade SDK feature-band releases in place. Feature bands are defined in the hundreds groups in the patch section of the version number. For example, 3.0.101 and 3.0.201 are versions in two different feature bands while 3.0.101 and 3.0.199 are in the same feature band. And, when .NET Core SDK 3.0.101 is installed, .NET Core SDK 3.0.100 will be removed from the machine if it exists. When .NET Core SDK 3.0.200 is installed on the same machine, .NET Core SDK 3.0.101 won't be removed.

For more information about versioning, see Overview of how .NET Core is versioned.

C# 8.0 preview

.NET Core 3.0 supports C# 8 preview. For more information about C# 8.0 features, see What's new in C# 8.0.

.NET Standard 2.1

Even though .NET Core 3.0 supports .NET Standard 2.1, the default dotnet new classlib template generates a project that targets .NET Standard 2.0. To target .NET Standard 2.1, edit your project file and change the TargetFramework property to netstandard2.1:

<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
  </PropertyGroup>
 
</Project>

If you're using Visual Studio, you need Visual Studio 2019, as Visual Studio 2017 doesn't support .NET Standard 2.1 or .NET Core 3.0.

Improved .NET Core Version APIs

Starting with .NET Core 3.0, the version APIs provided with .NET Core now return the information you expect. For example:

System.Console.WriteLine($"Environment.Version: {System.Environment.Version}");

// Old result
//   Environment.Version: 4.0.30319.42000
//
// New result
//   Environment.Version: 3.0.0
System.Console.WriteLine($"RuntimeInformation.FrameworkDescription: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");

// Old result
//   RuntimeInformation.FrameworkDescription: .NET Core 4.6.27415.71
//
// New result
//   RuntimeInformation.FrameworkDescription: .NET Core 3.0.0-preview4-27615-11

Warning

Breaking change. This is technically a breaking change because the versioning scheme has changed.

.NET Platform-Dependent Intrinsics

APIs have been added that allow access to certain perf-oriented CPU instructions, such as the SIMD or Bit Manipulation instruction sets. These instructions can help achieve significant performance improvements in certain scenarios, such as processing data efficiently in parallel.

Where appropriate, the .NET libraries have begun using these instructions to improve performance.

For more information, see .NET Platform Dependent Intrinsics.

Default executables

.NET Core now builds framework-dependent executables by default. This behavior is new for applications that use a globally installed version of .NET Core. Previously, only self-contained deployments would produce an executable.

During dotnet build or dotnet publish, an executable is created that matches the environment and platform of the SDK you're using. You can expect the same things with these executables as you would other native executables, such as:

  • You can double-click on the executable.
  • You can launch the application from a command prompt directly, such as myapp.exe on Windows, and ./myapp on Linux and macOS.

Single-file executables

The dotnet publish command supports packaging your app into a platform-specific single-file executable. The executable is self-extracting and contains all dependencies (including native) that are required to run your app. When the app is first run, the application is extracted to a directory based on the app name and build identifier. Startup is faster when the application is run again. The application doesn't need to extract itself a second time unless a new version was used.

To publish a single-file executable, set the PublishSingleFile in your project or on the command line with the dotnet publish command:

dotnet publish -r win10-x64 /p:PublishSingleFile=true

For more information about single-file publishing, see the single-file bundler design document.

Assembly linking

The .NET core 3.0 SDK comes with a tool that can reduce the size of apps by analyzing IL and trimming unused assemblies.

Self-contained apps include everything needed to run your code, without requiring .NET to be installed on the host computer. However, many times the app only requires a small subset of the framework to function, and other unused libraries could be removed.

.NET Core now includes a setting that will use the IL linker tool to scan the IL of your app. this tool detects what code is required, and then trims unused libraries. This tool can significantly reduce the deployment size of some apps.

To enable this tool, <PublishTrimmed> setting in your project and publish a self-contained app:

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
dotnet publish -r <rid> -c Release

As an example, the basic "hello world" new console project template that is included, when published, hits about 70 MB in size. By using <PublishTrimmed>, that size is reduced to about 30 MB.

It's important to consider that applications or frameworks (including ASP.NET Core and WPF) that use reflection or related dynamic features, will often break when trimmed. This breakage occurs because the linker doesn't know about this dynamic behavior and can't determine which framework types are required for reflection. The IL Linker tool can be configured to be aware of this scenario.

Above all else, be sure to test your app after trimming.

For more information about the IL Linker tool, see the documentation or visit the mono/linker repo.

Tiered compilation

Tiered compilation (TC) is on by default with .NET Core 3.0. This feature enables the runtime to more adaptively use the Just-In-Time (JIT) compiler to get better performance.

The main benefit of TC is to enable (re-)jitting methods with slower-but-faster to produce code or higher-quality-but-slower to produce code. This helps increase performance of an application as it goes through various stages of execution, from startup through steady-state. This contrasts with the non-TC approach, where every method is compiled a single way (the same as the high-quality tier), which is biased to steady-state over startup performance.

To enable Quick JIT (tier 0 jitted code), use this setting in your project file:

<PropertyGroup>
  <TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup>

To disable TC completely, use this setting in your project file:

<TieredCompilation>false</TieredCompilation>

ReadyToRun images

You can improve the startup time of your .NET Core application by compiling your application assemblies as ReadyToRun (R2R) format. R2R is a form of ahead-of-time (AOT) compilation.

R2R binaries improve startup performance by reducing the amount of work the just-in-time (JIT) compiler needs to do as your application loads. The binaries contain similar native code compared to what the JIT would produce.

R2R binaries are larger because they contain both intermediate language (IL) code, which is still needed for some scenarios, and the native version of the same code. R2R is only available when you publish a self-contained app that targets a specific runtime environments (RID) such as Linux x64 or Windows x64.

To compile your app as R2R, add the <PublishReadyToRun> setting:

<PropertyGroup>
  <PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>

Publish a self-contained app. For example, this command creates a self-contained app for the 64-bit version of Windows:

dotnet publish -c Release -r win-x64 --self-contained true

Cross platform/architecture restrictions

The ReadyToRun compiler doesn't currently support cross-targeting. You must compile on a given target. For example, if you want R2R images for Windows x64, you need to run the publish command on that environment.

Exceptions to cross-targeting:

  • Windows x64 can be used to compile Windows ARM32, ARM64, and x86 images.
  • Windows x86 can be used to compile Windows ARM32 images.
  • Linux x64 can be used to compile Linux ARM32 and ARM64 images.

Build copies dependencies

The dotnet build command now copies NuGet dependencies for your application from the NuGet cache to the build output folder. Previously, dependencies were only copied as part of dotnet publish.

There are some operations, like linking and razor page publishing that will still require publishing.

Local tools

.NET Core 3.0 introduces local tools. Local tools are similar to global tools but are associated with a particular location on disk. Local tools aren't available globally and are distributed as NuGet packages.

Warning

If you tried local tools in .NET Core 3.0 Preview 1, such as running dotnet tool restore or dotnet tool install, delete the local tools cache folder. Otherwise, local tools won't work on any newer release. This folder is located at:

On macOS, Linux: rm -r $HOME/.dotnet/toolResolverCache

On Windows: rmdir /s %USERPROFILE%\.dotnet\toolResolverCache

Local tools rely on a manifest file name dotnet-tools.json in your current directory. This manifest file defines the tools to be available at that folder and below. You can distribute the manifest file with your code to ensure that anyone who works with your code can restore and use the same tools.

For both global and local tools, a compatible version of the runtime is required. Many tools currently on NuGet.org target .NET Core Runtime 2.1. To install these tools globally or locally, you would still need to install the NET Core 2.1 Runtime.

Major-version Roll Forward

.NET Core 3.0 introduces an opt-in feature that allows your app to roll forward to the latest major version of .NET Core. Additionally, a new setting has been added to control how roll forward is applied to your app. This can be configured in the following ways:

  • Project file property: RollForward
  • Runtime configuration file property: rollForward
  • Environment variable: DOTNET_ROLL_FORWARD
  • Command-line argument: --roll-forward

One of the following values must be specified. If the setting is omitted, Minor is the default.

  • LatestPatch
    Roll forward to the highest patch version. This disables minor version roll forward.
  • Minor
    Roll forward to the lowest higher minor version, if requested minor version is missing. If the requested minor version is present, then the LatestPatch policy is used.
  • Major
    Roll forward to lowest higher major version, and lowest minor version, if requested major version is missing. If the requested major version is present, then the Minor policy is used.
  • LatestMinor
    Roll forward to highest minor version, even if requested minor version is present. Intended for component hosting scenarios.
  • LatestMajor
    Roll forward to highest major and highest minor version, even if requested major is present. Intended for component hosting scenarios.
  • Disable
    Don't roll forward. Only bind to specified version. This policy isn't recommended for general use because it disables the ability to roll forward to the latest patches. This value is only recommended for testing.

Besides the Disable setting, all settings will use the highest available patch version.

Windows desktop

.NET Core 3.0 supports Windows desktop applications using Windows Presentation Foundation (WPF) and Windows Forms. These frameworks also support using modern controls and Fluent styling from the Windows UI XAML Library (WinUI) via XAML islands.

The Windows Desktop component is part of the Windows .NET Core 3.0 SDK.

You can create a new WPF or Windows Forms app with the following dotnet commands:

dotnet new wpf
dotnet new winforms

Visual Studio 2019 adds New Project templates for .NET Core 3.0 Windows Forms and WPF.

For more information about how to port an existing .NET Framework application, see Port WPF projects and Port Windows Forms projects.

COM-callable components - Windows Desktop

On Windows, you can now create COM-callable managed components. This capability is critical to use .NET Core with COM add-in models and also to provide parity with .NET Framework.

Unlike .NET Framework where the mscoree.dll was used as the COM server, .NET Core will add a native launcher dll to the bin directory when you build your COM component.

For an example of how to create a COM component and consume it, see the COM Demo.

MSIX Deployment - Windows Desktop

MSIX is a new Windows application package format. It can be used to deploy .NET Core 3.0 desktop applications to Windows 10.

The Windows Application Packaging Project, available in Visual Studio 2019, allows you to create MSIX packages with self-contained .NET Core applications.

The .NET Core project file must specify the supported runtimes in the <RuntimeIdentifiers> property:

<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>

WinForms HighDPI

.NET Core Windows Forms applications can set High DPI mode with Application.SetHighDpiMode(HighDpiMode). The SetHighDpiMode method sets the corresponding High DPI mode unless the setting has been set by other means like App.Manifest or P/Invoke before Application.Run.

The possible highDpiMode values, as expressed by the System.Windows.Forms.HighDpiMode enum are:

  • DpiUnaware
  • SystemAware
  • PerMonitor
  • PerMonitorV2
  • DpiUnawareGdiScaled

For more information about High DPI modes, see High DPI Desktop Application Development on Windows.

Ranges and indices

The new System.Index type can be used for indexing. You can create one from an int that counts from the beginning, or with a prefix ^ operator (C#) that counts from the end:

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

There's also the System.Range type, which consists of two Index values, one for the start and one for the end, and can be written with a x..y range expression (C#). You can then index with a Range, which produces a slice:

var slice = a[i1..i2]; // { 3, 4, 5 }

For more information, see the ranges and indices tutorial.

Async streams

The IAsyncEnumerable<T> type is a new asynchronous version of IEnumerable<T>. The language lets you await foreach over IAsyncEnumerable<T> to consume their elements, and use yield return to them to produce elements.

The following example demonstrates both production and consumption of async streams. The foreach statement is async and itself uses yield return to produce an async stream for callers. This pattern (using yield return) is the recommended model for producing async streams.

async IAsyncEnumerable<int> GetBigResultsAsync()
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result; 
    }
}

In addition to being able to await foreach, you can also create async iterators, for example, an iterator that returns an IAsyncEnumerable/IAsyncEnumerator that you can both await and yield in. For objects that need to be disposed, you can use IAsyncDisposable, which various BCL types implement, such as Stream and Timer.

For more information, see the async streams tutorial.

IEEE Floating-point improvements

Floating point APIs are being updated to comply with IEEE 754-2008 revision. The goal of these changes is to expose all required operations and ensure that they're behaviorally compliant with the IEEE spec. For more information about floating-point improvements, see the Floating-Point Parsing and Formatting improvements in .NET Core 3.0 blog post.

Parsing and formatting fixes include:

  • Correctly parse and round inputs of any length.
  • Correctly parse and format negative zero.
  • Correctly parse Infinity and NaN by doing a case-insensitive check and allowing an optional preceding + where applicable.

New System.Math APIs include:

  • BitIncrement(Double) and BitDecrement(Double)
    Corresponds to the nextUp and nextDown IEEE operations. They return the smallest floating-point number that compares greater or lesser than the input (respectively). For example, Math.BitIncrement(0.0) would return double.Epsilon.

  • MaxMagnitude(Double, Double) and MinMagnitude(Double, Double)
    Corresponds to the maxNumMag and minNumMag IEEE operations, they return the value that is greater or lesser in magnitude of the two inputs (respectively). For example, Math.MaxMagnitude(2.0, -3.0) would return -3.0.

  • ILogB(Double)
    Corresponds to the logB IEEE operation that returns an integral value, it returns the integral base-2 log of the input parameter. This method is effectively the same as floor(log2(x)), but done with minimal rounding error.

  • ScaleB(Double, Int32)
    Corresponds to the scaleB IEEE operation that takes an integral value, it returns effectively x * pow(2, n), but is done with minimal rounding error.

  • Log2(Double)
    Corresponds to the log2 IEEE operation, it returns the base-2 logarithm. It minimizes rounding error.

  • FusedMultiplyAdd(Double, Double, Double)
    Corresponds to the fma IEEE operation, it performs a fused multiply add. That is, it does (x * y) + z as a single operation, there-by minimizing the rounding error. An example would be FusedMultiplyAdd(1e308, 2.0, -1e308) which returns 1e308. The regular (1e308 * 2.0) - 1e308 returns double.PositiveInfinity.

  • CopySign(Double, Double)
    Corresponds to the copySign IEEE operation, it returns the value of x, but with the sign of y.

Fast built-in JSON support

.NET users have largely relied on Json.NET and other popular JSON libraries, which continue to be good choices. Json.NET uses .NET strings as its base datatype, which is UTF-16 under the hood.

The new built-in JSON support is high-performance, low allocation, and based on Span<byte>. Three new main JSON-related types have been added to .NET Core 3.0 the System.Text.Json namespace. These types don't yet support plain old CLR object (POCO) serialization and deserialization.

Utf8JsonReader

System.Text.Json.Utf8JsonReader is a high-performance, low allocation, forward-only reader for UTF-8 encoded JSON text, read from a ReadOnlySpan<byte>. The Utf8JsonReader is a foundational, low-level type, that can be used to build custom parsers and deserializers. Reading through a JSON payload using the new Utf8JsonReader is 2x faster than using the reader from Json.NET. It doesn't allocate until you need to actualize JSON tokens as (UTF-16) strings.

Here is an example of reading through the launch.json file created by Visual Studio Code:

public static void PrintJson(ReadOnlySpan<byte> dataUtf8)
{
    var json = new Utf8JsonReader(dataUtf8, isFinalBlock: true, state: default);
    
    while (json.Read())
    {
        JsonTokenType tokenType = json.TokenType;
        ReadOnlySpan<byte> valueSpan = json.ValueSpan;
        switch (tokenType)
        {
            case JsonTokenType.StartObject:
            case JsonTokenType.EndObject:
                break;
            case JsonTokenType.StartArray:
            case JsonTokenType.EndArray:
                break;
            case JsonTokenType.PropertyName:
                break;
            case JsonTokenType.String:
                Console.WriteLine($"STRING: {json.GetString()}");
                break;
            case JsonTokenType.Number:
                if (!json.TryGetInt32(out int valueInteger))
                {
                    throw new FormatException();
                }
                break;
            case JsonTokenType.True:
            case JsonTokenType.False:
                Console.WriteLine($"BOOL: {json.GetBoolean()}");
                break;
            case JsonTokenType.Null:
                break;
            default:
                throw new ArgumentException();
        }
    }

    dataUtf8 = dataUtf8.Slice((int)json.BytesConsumed);
    JsonReaderState state = json.CurrentState;
}
// Calling code
Console.WriteLine("Read with Utf8JsonReader");
PrintJson(File.ReadAllBytes("launch.json").AsSpan());

Utf8JsonWriter

System.Text.Json.Utf8JsonWriter provides a high-performance, non-cached, forward-only way to write UTF-8 encoded JSON text from common .NET types like String, Int32, and DateTime. Like the reader, the writer is a foundational, low-level type, that can be used to build custom serializers. Writing a JSON payload using the new Utf8JsonWriter is 30-80% faster than using the writer from Json.NET and doesn't allocate.

JsonDocument

System.Text.Json.JsonDocument is built on top of the Utf8JsonReader. The JsonDocument provides the ability to parse JSON data and build a read-only Document Object Model (DOM) that can be queried to support random access and enumeration. The JSON elements that compose the data can be accessed via the JsonElement type that is exposed by the JsonDocument as a property called RootElement. The JsonElement contains the JSON array and object enumerators along with APIs to convert JSON text to common .NET types. Parsing a typical JSON payload and accessing all its members using the JsonDocument is 2-3x faster than Json.NET with little allocations for data that is reasonably sized (that is, < 1 MB).

Here is a sample usage of the JsonDocument and JsonElement that can be used as a starting point:

Here is a C# 8.0 example of reading through the launch.json file created by Visual Studio Code:

public static void ReadJson(string jsonString)
{
    using var document = JsonDocument.Parse(jsonString);

    var root = document.RootElement;
    var version = root.GetProperty("version").GetString();
    var configurations = root.GetProperty("configurations");

    Console.WriteLine($"Launch Version: {version}");

    foreach (var config in configurations.EnumerateArray())
    {
        var name = config.GetProperty("name").GetString();
        Console.WriteLine($"Config: {name}");
    }
}
// Calling code
Console.WriteLine("Read with JsonDocument");
ReadJson(File.ReadAllText("launch.json"));

JsonSerializer

System.Text.Json.Serialization.JsonSerializer is built on top of Utf8JsonReader and Utf8JsonWriter to provide a fast low-memory serialization option when working with JSON documents and fragments.

EXAMINE: https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/docs/SerializerProgrammingModel.md for an example to port to this article

Here is an example of serializing an object to JSON:

var instance = new
{
    FirstName = "John",
    LastName = "Smith",
    Age = 18
};

System.Console.WriteLine(System.Text.Json.Serialization.JsonSerializer.ToString(instance));

Here is an example of deserializing a JSON string to an object. You can use the JSON string produced by the previous example:

public class JsonPerson
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public static JsonPerson Parse(string json) =>
        System.Text.Json.Serialization.JsonSerializer.Parse<JsonPerson>(json);
}

Interop improvements

.NET Core 3.0 improves native API interop.

Type: NativeLibrary

System.Runtime.InteropServices.NativeLibrary provides an encapsulation for loading a native library (using the same load logic as .NET Core P/Invoke) and providing the relevant helper functions such as getSymbol. For a code example, see the DLLMap Demo.

Windows Native Interop

Windows offers a rich native API in the form of flat C APIs, COM, and WinRT. While .NET Core supports P/Invoke, .NET Core 3.0 adds the ability to CoCreate COM APIs and Activate WinRT APIs. For a code example, see the Excel Demo.

HTTP/2 support

The System.Net.Http.HttpClient type supports the HTTP/2 protocol. If HTTP/2 is enabled, the HTTP protocol version is negotiated via TLS/ALPN, and HTTP/2 is used if the server elects to use it.

The default protocol remains HTTP/1.1, but HTTP/2 can be enabled in two different ways. First, you can set the HTTP request message to use HTTP/2:

var client = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };

// HTTP/1.1 request
using (var response = await client.GetAsync("/"))
    Console.WriteLine(response.Content);

// HTTP/2 request
using (var request = new HttpRequestMessage(HttpMethod.Get, "/") { Version = new Version(2, 0) })
using (var response = await client.SendAsync(request))
    Console.WriteLine(response.Content);

Second, you can change HttpClient to use HTTP/2 by default:

var client = new HttpClient()
{
    BaseAddress = new Uri("https://localhost:5001"),
    DefaultRequestVersion = new Version(2, 0)
};

// HTTP/2 is default
using (var response = await client.GetAsync("/"))
    Console.WriteLine(response.Content);

Many times when you're developing an application, you want to use an unencrypted connection. If you know the target endpoint will be using HTTP/2, you can turn on unencrypted connections for HTTP/2. You can turn it on by setting the DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT environment variable to 1 or by enabling it in the app context:

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

TLS 1.3 & OpenSSL 1.1.1 on Linux

.NET Core now takes advantage of TLS 1.3 support in OpenSSL 1.1.1, when it's available in a given environment. With TLS 1.3:

  • Connection times are improved with reduced round trips required between the client and server.
  • Improved security because of the removal of various obsolete and insecure cryptographic algorithms.

When available, .NET Core 3.0 uses OpenSSL 1.1.1, OpenSSL 1.1.0, or OpenSSL 1.0.2 on a Linux system. When OpenSSL 1.1.1 is available, both System.Net.Security.SslStream and System.Net.Http.HttpClient types will use TLS 1.3 (assuming both the client and server support TLS 1.3).

Important

Windows and macOS do not yet support TLS 1.3. .NET Core 3.0 will support TLS 1.3 on these operating systems when support becomes available.

The following C# 8.0 example demonstrates .NET Core 3.0 on Ubuntu 18.10 connecting to https://www.cloudflare.com:

using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace whats_new
{
    public static class TLS
    {
        public static async Task ConnectCloudFlare()
        {
            var targetHost = "www.cloudflare.com";

            using TcpClient tcpClient = new TcpClient();

            await tcpClient.ConnectAsync(targetHost, 443);

            using SslStream sslStream = new SslStream(tcpClient.GetStream());
            
            await sslStream.AuthenticateAsClientAsync(targetHost);
            await Console.Out.WriteLineAsync($"Connected to {targetHost} with {sslStream.SslProtocol}");
        }
    }
}

Cryptography ciphers

.NET 3.0 adds support for AES-GCM and AES-CCM ciphers, implemented with System.Security.Cryptography.AesGcm and System.Security.Cryptography.AesCcm respectively. These algorithms are both Authenticated Encryption with Association Data (AEAD) algorithms.

The following code demonstrates using AesGcm cipher to encrypt and decrypt random data.

using System;
using System.Linq;
using System.Security.Cryptography;

namespace whats_new
{
    public static class Cipher
    {
        public static void Run()
        {
            // key should be: pre-known, derived, or transported via another channel, such as RSA encryption
            byte[] key = new byte[16];
            RandomNumberGenerator.Fill(key);

            byte[] nonce = new byte[12];
            RandomNumberGenerator.Fill(nonce);

            // normally this would be your data
            byte[] dataToEncrypt = new byte[1234];
            byte[] associatedData = new byte[333];
            RandomNumberGenerator.Fill(dataToEncrypt);
            RandomNumberGenerator.Fill(associatedData);

            // these will be filled during the encryption
            byte[] tag = new byte[16];
            byte[] ciphertext = new byte[dataToEncrypt.Length];

            using (AesGcm aesGcm = new AesGcm(key))
            {
                aesGcm.Encrypt(nonce, dataToEncrypt, ciphertext, tag, associatedData);
            }

            // tag, nonce, ciphertext, associatedData should be sent to the other part

            byte[] decryptedData = new byte[ciphertext.Length];

            using (AesGcm aesGcm = new AesGcm(key))
            {
                aesGcm.Decrypt(nonce, ciphertext, tag, decryptedData, associatedData);
            }

            // do something with the data
            // this should always print that data is the same
            Console.WriteLine($"AES-GCM: Decrypted data is {(dataToEncrypt.SequenceEqual(decryptedData) ? "the same as" : "different than")} original data.");
        }
    }
}

Cryptographic Key Import/Export

.NET Core 3.0 supports the import and export of asymmetric public and private keys from standard formats. You don't need to use an X.509 certificate.

All key types, such as RSA, DSA, ECDsa, and ECDiffieHellman, support the following formats:

  • Public Key

    • X.509 SubjectPublicKeyInfo
  • Private key

    • PKCS#8 PrivateKeyInfo
    • PKCS#8 EncryptedPrivateKeyInfo

RSA keys also support:

  • Public Key

    • PKCS#1 RSAPublicKey
  • Private key

    • PKCS#1 RSAPrivateKey

The export methods produce DER-encoded binary data, and the import methods expect the same. If a key is stored in the text-friendly PEM format, the caller will need to base64-decode the content before calling an import method.

using System;
using System.Security.Cryptography;

namespace whats_new
{
    public static class RSATest
    {
        public static void Run(string keyFile)
        {
            using var rsa = RSA.Create();

            byte[] keyBytes = System.IO.File.ReadAllBytes(keyFile);
            rsa.ImportRSAPrivateKey(keyBytes, out int bytesRead);

            Console.WriteLine($"Read {bytesRead} bytes, {keyBytes.Length - bytesRead} extra byte(s) in file.");
            RSAParameters rsaParameters = rsa.ExportParameters(true);
            Console.WriteLine(BitConverter.ToString(rsaParameters.D));
        }
    }
}

PKCS#8 files can be inspected with System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo and PFX/PKCS#12 files can be inspected with System.Security.Cryptography.Pkcs.Pkcs12Info. PFX/PKCS#12 files can be manipulated with System.Security.Cryptography.Pkcs.Pkcs12Builder.

SerialPort for Linux

.NET Core 3.0 supports System.IO.Ports.SerialPort on Linux.

Previously, .NET Core only supported using SerialPort on Windows.

Docker and cgroup memory Limits

Starting with Preview 3, running .NET Core 3.0 on Linux with Docker works better with cgroup memory limits. Running a Docker container with memory limits, such as with docker run -m, changes how .NET Core behaves.

  • Default Garbage Collector (GC) heap size: maximum of 20 mb or 75% of the memory limit on the container.
  • Explicit size can be set as an absolute number or percentage of cgroup limit.
  • Minimum reserved segment size per GC heap is 16 mb. This size reduces the number of heaps that are created on machines.

Smaller Garbage Collection heap sizes

The Garbage Collector's default heap size has been reduced resulting in .NET Core using less memory. This change better aligns with the generation 0 allocation budget with modern processor cache sizes.

Garbage Collection Large Page support

Large Pages (also known as Huge Pages on Linux) is a feature where the operating system is able to establish memory regions larger than the native page size (often 4K) to improve performance of the application requesting these large pages.

The Garbage Collector can now be configured with the GCLargePages setting as an opt-in feature to choose to allocate large pages on Windows.

GPIO Support for Raspberry Pi

Two packages have been released to NuGet that you can use for GPIO programming:

The GPIO packages include APIs for GPIO, SPI, I2C, and PWM devices. The IoT bindings package includes device bindings. For more information, see the devices GitHub repo.

ARM64 Linux support

.NET Core 3.0 adds support for ARM64 for Linux. The primary use case for ARM64 is currently with IoT scenarios. For more information, see .NET Core ARM64 Status.

Docker images for .NET Core on ARM64 are available for Alpine, Debian, and Ubuntu.

Note

ARM64 Windows support isn't yet available.