Skip to content

Commit

Permalink
[AndroidCrypto] Enable using X.509 certs with DSA (#49394)
Browse files Browse the repository at this point in the history
* [AndroidCrypto] Enable using X.509 certs with DSA

* Refactor key pair creation.

* Only PinAndClear X.

* Update src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/X509Pal.cs

Co-authored-by: Jeremy Barton <jbarton@microsoft.com>

* Alphabetize the sources in the csproj.

* Add missing include.

* Add missing parameter

* Add missing DuplicateHandle call.

Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
  • Loading branch information
jkoritzinsky and bartonjs committed Mar 11, 2021
1 parent d3e69e0 commit b341185
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,31 @@ internal static DSAParameters ExportDsaParameters(SafeDsaHandle key, bool includ

namespace System.Security.Cryptography
{
internal sealed class SafeDsaHandle : Interop.JObjectLifetime.SafeJObjectHandle
internal sealed class SafeDsaHandle : SafeKeyHandle
{
public SafeDsaHandle()
{
}

internal SafeDsaHandle(IntPtr ptr)
: base(ptr)
{
SetHandle(ptr);
}

protected override bool ReleaseHandle()
{
Interop.JObjectLifetime.DeleteGlobalReference(handle);
SetHandle(IntPtr.Zero);
return true;
}

internal static SafeDsaHandle DuplicateHandle(IntPtr handle)
{
Debug.Assert(handle != IntPtr.Zero);

return new SafeDsaHandle(Interop.JObjectLifetime.NewGlobalReference(handle));
}

internal override SafeDsaHandle DuplicateHandle() => DuplicateHandle(DangerousGetHandle());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public DSAAndroid(int keySize)
_key = new Lazy<SafeDsaHandle>(GenerateKey);
}

internal DSAAndroid(SafeDsaHandle key)
{
SetKey(key.DuplicateHandle());
}

public override int KeySize
{
set
Expand Down Expand Up @@ -384,6 +389,8 @@ private void SetKey(SafeDsaHandle newKey)
_key = new Lazy<SafeDsaHandle>(newKey);
}

internal SafeDsaHandle DuplicateKeyHandle() => _key.Value.DuplicateHandle();

private static readonly KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(minSize: 1024, maxSize: 3072, skipSize: 1024) };
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "pal_utilities.h"
#include "pal_signature.h"
#include "pal_bignum.h"
#include "pal_misc.h"

#define INIT_LOCALS(name, ...) \
enum { __VA_ARGS__, count_##name }; \
Expand Down Expand Up @@ -311,8 +312,7 @@ int32_t AndroidCryptoNative_DsaKeyCreateByExplicitParameters(
ON_EXCEPTION_PRINT_AND_GOTO(error);
}

jobject keyPair = (*env)->NewObject(env, g_keyPairClass, g_keyPairCtor, loc[publicKey], loc[privateKey]);
*outDsa = ToGRef(env, keyPair);
*outDsa = AndroidCryptoNative_CreateKeyPair(env, loc[publicKey], loc[privateKey]);
if (CheckJNIExceptions(env))
{
ON_EXCEPTION_PRINT_AND_GOTO(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "pal_eckey.h"
#include "pal_jni.h"
#include "pal_utilities.h"
#include "pal_misc.h"


#define INIT_LOCALS(name, ...) \
Expand Down Expand Up @@ -322,7 +323,7 @@ static jobject AndroidCryptoNative_CreateKeyPairFromCurveParameters(
loc[privateKey] = (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPrivateMethod, loc[privKeySpec]);
ON_EXCEPTION_PRINT_AND_GOTO(error);
}
keyPair = (*env)->NewObject(env, g_keyPairClass, g_keyPairCtor, loc[publicKey], loc[privateKey]);
keyPair = AndroidCryptoNative_CreateKeyPair(env, loc[publicKey], loc[privateKey]);

goto cleanup;

Expand All @@ -337,7 +338,7 @@ static jobject AndroidCryptoNative_CreateKeyPairFromCurveParameters(
cleanup:
RELEASE_LOCALS_ENV(bn, ReleaseGRef);
RELEASE_LOCALS_ENV(loc, ReleaseLRef);
return ToGRef(env, keyPair);
return keyPair;
}

int32_t AndroidCryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#include "pal_eckey.h"
#include "pal_misc.h"

#include <assert.h>

Expand All @@ -26,8 +27,7 @@ EC_KEY* AndroidCryptoNative_NewEcKeyFromPublicKey(JNIEnv *env, jobject /*ECPubli
return NULL;

jobject curveParameters = (*env)->CallObjectMethod(env, publicKey, g_ECPublicKeyGetParams);
jobject keyPair = (*env)->NewObject(env, g_keyPairClass, g_keyPairCtor, publicKey, NULL);
return AndroidCryptoNative_NewEcKey(ToGRef(env, curveParameters), ToGRef(env, keyPair));
return AndroidCryptoNative_NewEcKey(ToGRef(env, curveParameters), AndroidCryptoNative_CreateKeyPair(env, publicKey, NULL));
}

#pragma clang diagnostic push
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#include "pal_misc.h"
#include "pal_jni.h"

int32_t CryptoNative_EnsureOpenSslInitialized()
{
Expand All @@ -26,3 +27,9 @@ int32_t CryptoNative_GetRandomBytes(uint8_t* buff, int32_t len)

return CheckJNIExceptions(env) ? FAIL : SUCCESS;
}

jobject AndroidCryptoNative_CreateKeyPair(JNIEnv* env, jobject publicKey, jobject privateKey)
{
jobject keyPair = (*env)->NewObject(env, g_keyPairClass, g_keyPairCtor, publicKey, privateKey);
return CheckJNIExceptions(env) ? FAIL : ToGRef(env, keyPair);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@

PALEXPORT int32_t CryptoNative_EnsureOpenSslInitialized(void);
PALEXPORT int32_t CryptoNative_GetRandomBytes(uint8_t* buf, int32_t num);

jobject AndroidCryptoNative_CreateKeyPair(JNIEnv* env, jobject publicKey, jobject privateKey);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "pal_eckey.h"
#include "pal_rsa.h"
#include "pal_misc.h"

#include <assert.h>
#include <stdbool.h>
Expand Down Expand Up @@ -260,7 +261,7 @@ void* AndroidCryptoNative_X509PublicKey(jobject /*X509Certificate*/ cert, PAL_Ke
keyHandle = AndroidCryptoNative_NewEcKeyFromPublicKey(env, key);
break;
case PAL_DSA:
keyHandle = NULL;
keyHandle = AndroidCryptoNative_CreateKeyPair(env, key, NULL);
break;
case PAL_RSA:
keyHandle = AndroidCryptoNative_NewRsaFromPublicKey(env, key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,14 @@ internal void SetPrivateKey(SafeKeyHandle privateKey)

public DSA? GetDSAPrivateKey()
{
throw new NotImplementedException(nameof(GetDSAPrivateKey));
if (_privateKey == null || _privateKey.IsInvalid)
return null;

SafeDsaHandle? dsaKey = _privateKey as SafeDsaHandle;
if (dsaKey == null)
throw new CryptographicException();

return new DSAImplementation.DSAAndroid(dsaKey);
}

public ECDsa? GetECDsaPrivateKey()
Expand Down Expand Up @@ -386,7 +393,19 @@ internal void SetPrivateKey(SafeKeyHandle privateKey)

public ICertificatePal CopyWithPrivateKey(DSA privateKey)
{
throw new NotImplementedException($"{nameof(CopyWithPrivateKey)}(DSA)");
DSAImplementation.DSAAndroid? typedKey = privateKey as DSAImplementation.DSAAndroid;
if (typedKey != null)
{
return CopyWithPrivateKeyHandle(typedKey.DuplicateKeyHandle());
}

DSAParameters dsaParameters = privateKey.ExportParameters(true);
using (PinAndClear.Track(dsaParameters.X!))
using (typedKey = new DSAImplementation.DSAAndroid())
{
typedKey.ImportParameters(dsaParameters);
return CopyWithPrivateKeyHandle(typedKey.DuplicateKeyHandle());
};
}

public ICertificatePal CopyWithPrivateKey(ECDsa privateKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory<byte> pkcs8)
key = new RSAImplementation.RSAAndroid();
break;
case Oids.Dsa:
// TODO: [AndroidCrypto] Handle DSA
throw new NotImplementedException($"{nameof(LoadKey)} ({algorithm})");
key = new DSAImplementation.DSAAndroid();
break;
case Oids.EcDiffieHellman:
case Oids.EcPublicKey:
key = new ECDsaImplementation.ECDsaAndroid();
Expand Down Expand Up @@ -85,7 +85,11 @@ internal static SafeKeyHandle GetPrivateKey(AsymmetricAlgorithm key)
return rsa.DuplicateKeyHandle();
}

// TODO: [AndroidCrypto] Handle DSA
if (key is DSAImplementation.DSAAndroid dsa)
{
return dsa.DuplicateKeyHandle();
}

throw new NotImplementedException($"{nameof(GetPrivateKey)} ({key.GetType()})");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOn
case SafeRsaHandle rsa:
algorithm = new RSAImplementation.RSAAndroid(rsa);
break;
case SafeDsaHandle dsa:
algorithm = new DSAImplementation.DSAAndroid(dsa);
break;
default:
throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Formats.Asn1;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.X509Certificates;

namespace Internal.Cryptography.Pal
Expand Down Expand Up @@ -41,7 +43,15 @@ public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certifica
switch (oid.Value)
{
case Oids.Dsa:
throw new NotImplementedException($"{nameof(DecodePublicKey)} (DSA)");
if (certificatePal != null)
{
var handle = new SafeDsaHandle(GetPublicKey(certificatePal, Interop.AndroidCrypto.PAL_KeyAlgorithm.DSA));
return new DSAImplementation.DSAAndroid(handle);
}
else
{
return DecodeDsaPublicKey(encodedKeyValue, encodedParameters);
}
case Oids.Rsa:
if (certificatePal != null)
{
Expand Down Expand Up @@ -130,6 +140,37 @@ private static RSA DecodeRsaPublicKey(byte[] encodedKeyValue)
throw;
}
}

private static DSA DecodeDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters)
{
SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn
{
Algorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Dsa, Parameters = encodedParameters },
SubjectPublicKey = encodedKeyValue,
};

AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
spki.Encode(writer);

byte[] rented = CryptoPool.Rent(writer.GetEncodedLength());

int written = writer.Encode(rented);

DSA dsa = DSA.Create();
IDisposable? toDispose = dsa;

try
{
dsa.ImportSubjectPublicKeyInfo(rented.AsSpan(0, written), out _);
toDispose = null;
return dsa;
}
finally
{
toDispose?.Dispose();
CryptoPool.Return(rented, written);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@
Link="Common\Interop\Android\Interop.JObjectLifetime.cs" />
<Compile Include="$(CommonPath)Interop\Android\System.Security.Cryptography.Native.Android\Interop.Bignum.cs"
Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\Interop.Bignum.cs" />
<Compile Include="$(CommonPath)Interop\Android\System.Security.Cryptography.Native.Android\Interop.Dsa.cs"
Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\Interop.Dsa.cs" />
<Compile Include="$(CommonPath)Interop\Android\System.Security.Cryptography.Native.Android\Interop.Ecdh.cs"
Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\Interop.Ecdh.cs" />
<Compile Include="$(CommonPath)Interop\Android\System.Security.Cryptography.Native.Android\Interop.EcDsa.cs"
Expand All @@ -426,6 +428,8 @@
Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\SafeKeyHandle.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\DSAAndroid.cs"
Link="Common\System\Security\Cryptography\DSAAndroid.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\ECAndroid.cs"
Link="Common\System\Security\Cryptography\ECAndroid.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\ECAndroid.ImportExport.cs"
Expand Down

0 comments on commit b341185

Please sign in to comment.