Skip to content

Commit

Permalink
OpenSSL ENGINE support (#88656)
Browse files Browse the repository at this point in the history
* OpenSSL ENGINE support

* Remove trailing spaces on README file

* Address PR feedback

* Test for PNSE and run tests on all platforms supporting OSSL

* Update IsOpenSslSupported

* s/IsOpenSslSupported/OpenSslPresentOnSystem

* Fix OpenSslNotPresentOnSystem on Windows

* Add preventive OpenSslIsAvailable check for better error handling
  • Loading branch information
krwq authored Jul 18, 2023
1 parent 1deddde commit 420dd4e
Show file tree
Hide file tree
Showing 18 changed files with 898 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,52 @@ internal static ArraySegment<byte> RentEncodeSubjectPublicKeyInfo(SafeEvpPKeyHan
}
}

[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpPKeyHandle CryptoNative_LoadPrivateKeyFromEngine(
string engineName,
string keyName);

internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine(
string engineName,
string keyName)
{
Debug.Assert(engineName is not null);
Debug.Assert(keyName is not null);

SafeEvpPKeyHandle pkey = CryptoNative_LoadPrivateKeyFromEngine(engineName, keyName);

if (pkey.IsInvalid)
{
pkey.Dispose();
throw CreateOpenSslCryptographicException();
}

return pkey;
}

[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpPKeyHandle CryptoNative_LoadPublicKeyFromEngine(
string engineName,
string keyName);

internal static SafeEvpPKeyHandle LoadPublicKeyFromEngine(
string engineName,
string keyName)
{
Debug.Assert(engineName is not null);
Debug.Assert(keyName is not null);

SafeEvpPKeyHandle pkey = CryptoNative_LoadPublicKeyFromEngine(engineName, keyName);

if (pkey.IsInvalid)
{
pkey.Dispose();
throw CreateOpenSslCryptographicException();
}

return pkey;
}

internal enum EvpAlgorithmId
{
Unknown = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static bool OpenSslPresentOnSystem
{
get
{
if (IsAndroid || UsesMobileAppleCrypto || IsBrowser)
if (IsWindows || IsAndroid || UsesMobileAppleCrypto || IsBrowser)
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ public static bool IsMetadataTokenSupported
public static bool IsNotDomainJoinedMachine => !IsDomainJoinedMachine;

public static bool IsOpenSslSupported => IsLinux || IsFreeBSD || Isillumos || IsSolaris;
public static bool OpenSslNotPresentOnSystem => !OpenSslPresentOnSystem;

public static bool UsesAppleCrypto => IsOSX || IsMacCatalyst || IsiOS || IstvOS;
public static bool UsesMobileAppleCrypto => IsMacCatalyst || IsiOS || IstvOS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2245,6 +2245,18 @@ public sealed partial class SafeEvpPKeyHandle : System.Runtime.InteropServices.S
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
public static long OpenSslVersion { get { throw null; } }
public System.Security.Cryptography.SafeEvpPKeyHandle DuplicateHandle() { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
public static System.Security.Cryptography.SafeEvpPKeyHandle OpenPrivateKeyFromEngine(string engineName, string keyId) { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
public static System.Security.Cryptography.SafeEvpPKeyHandle OpenPublicKeyFromEngine(string engineName, string keyId) { throw null; }
protected override bool ReleaseHandle() { throw null; }
}
public abstract partial class SHA1 : System.Security.Cryptography.HashAlgorithm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,22 @@ public SafeEvpPKeyHandle(IntPtr handle, bool ownsHandle) : base(handle, ownsHand
public static long OpenSslVersion =>
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL);

[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenPrivateKeyFromEngine(string engineName, string keyId) =>
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL);

[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenPublicKeyFromEngine(string engineName, string keyId) =>
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL);

public SafeEvpPKeyHandle DuplicateHandle() => null!;
public override bool IsInvalid => true;
protected override bool ReleaseHandle() => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,110 @@ public SafeEvpPKeyHandle DuplicateHandle()
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static long OpenSslVersion { get; } = Interop.OpenSsl.OpenSslVersionNumber();

/// <summary>
/// Open a named private key using a named OpenSSL <code>ENGINE</code>.
/// </summary>
/// <param name="engineName">
/// The name of the <code>ENGINE</code> to process the private key open request.
/// </param>
/// <param name="keyId">
/// The name of the key to open.
/// </param>
/// <returns>
/// The opened key.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="engineName"/> or <paramref name="keyId"/> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="engineName"/> or <paramref name="keyId"/> is the empty string.
/// </exception>
/// <exception cref="CryptographicException">
/// the key could not be opened via the specified ENGINE.
/// </exception>
/// <remarks>
/// <para>
/// This operation will fail if OpenSSL cannot successfully load the named <code>ENGINE</code>,
/// or if the named <code>ENGINE</code> cannot load the named key.
/// </para>
/// <para>
/// Not all <code>ENGINE</code>s support loading private keys.
/// </para>
/// <para>
/// The syntax for <paramref name="keyId"/> is determined by each individual
/// <code>ENGINE</code>.
/// </para>
/// </remarks>
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenPrivateKeyFromEngine(string engineName, string keyId)
{
ArgumentException.ThrowIfNullOrEmpty(engineName);
ArgumentException.ThrowIfNullOrEmpty(keyId);

if (!Interop.OpenSslNoInit.OpenSslIsAvailable)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL);
}

return Interop.Crypto.LoadPrivateKeyFromEngine(engineName, keyId);
}

/// <summary>
/// Open a named public key using a named OpenSSL <code>ENGINE</code>.
/// </summary>
/// <param name="engineName">
/// The name of the <code>ENGINE</code> to process the public key open request.
/// </param>
/// <param name="keyId">
/// The name of the key to open.
/// </param>
/// <returns>
/// The opened key.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="engineName"/> or <paramref name="keyId"/> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="engineName"/> or <paramref name="keyId"/> is the empty string.
/// </exception>
/// <exception cref="CryptographicException">
/// the key could not be opened via the specified ENGINE.
/// </exception>
/// <remarks>
/// <para>
/// This operation will fail if OpenSSL cannot successfully load the named <code>ENGINE</code>,
/// or if the named <code>ENGINE</code> cannot load the named key.
/// </para>
/// <para>
/// Not all <code>ENGINE</code>s support loading public keys, even ones that support
/// loading private keys.
/// </para>
/// <para>
/// The syntax for <paramref name="keyId"/> is determined by each individual
/// <code>ENGINE</code>.
/// </para>
/// </remarks>
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("windows")]
public static SafeEvpPKeyHandle OpenPublicKeyFromEngine(string engineName, string keyId)
{
ArgumentException.ThrowIfNullOrEmpty(engineName);
ArgumentException.ThrowIfNullOrEmpty(keyId);

if (!Interop.OpenSslNoInit.OpenSslIsAvailable)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyOpenSSL);
}

return Interop.Crypto.LoadPublicKeyFromEngine(engineName, keyId);
}
}
}
Loading

0 comments on commit 420dd4e

Please sign in to comment.