Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add strong naming support for netstandard #803

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Mono.Cecil/ModuleDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
using System.IO;
using System.Threading;
using SR = System.Reflection;
#if (NETSTANDARD)
using StrongNameKeyPair=Mono.Security.Cryptography.StrongNameKeyPair;
#else
using StrongNameKeyPair=System.Reflection.StrongNameKeyPair;
#endif
Comment on lines +16 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates a different and incompatible API shape between the .NET Framework and .NET Standard editions of Cecil and will break if an assembly compiled with the former uses the latter at runtime (a totally possible and supported scenario).

I had started and abandoned work on a compatible design that would solve this problem. It can be found in https://github.com/teo-tsirpanis/cecil/tree/strong-name-key-pair.


using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
Expand Down Expand Up @@ -185,7 +190,7 @@ public sealed class WriterParameters {
bool write_symbols;
byte [] key_blob;
string key_container;
SR.StrongNameKeyPair key_pair;
StrongNameKeyPair key_pair;

public uint? Timestamp {
get { return timestamp; }
Expand Down Expand Up @@ -221,7 +226,7 @@ public string StrongNameKeyContainer {
set { key_container = value; }
}

public SR.StrongNameKeyPair StrongNameKeyPair {
public StrongNameKeyPair StrongNameKeyPair {
get { return key_pair; }
set { key_pair = value; }
}
Expand Down
132 changes: 132 additions & 0 deletions Mono.Cecil/StrongNameKeyPair.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#if (NETSTANDARD)
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Cryptography;

namespace Mono.Security.Cryptography {
[Serializable]
public class StrongNameKeyPair : ISerializable {
bool _keyPairExported;
byte [] _keyPairArray;
string _keyPairContainer;
byte [] _publicKey;

public byte [] PublicKey {
[SecuritySafeCritical]
get {
if (_publicKey == null) {
_publicKey = ComputePublicKey ();
}

byte [] array = new byte[_publicKey.Length];
Array.Copy (_publicKey, array, _publicKey.Length);
return array;
}
}

public StrongNameKeyPair (FileStream keyPairFile)
{
if (keyPairFile == null) {
throw new ArgumentNullException ("keyPairFile");
}

var num = (int) keyPairFile.Length;
_keyPairArray = new byte[num];
keyPairFile.Read (_keyPairArray, 0, num);
_keyPairExported = true;
}

public StrongNameKeyPair (byte [] keyPairArray)
{
if (keyPairArray == null) {
throw new ArgumentNullException ("keyPairArray");
}

_keyPairArray = new byte[keyPairArray.Length];
Array.Copy (keyPairArray, _keyPairArray, keyPairArray.Length);
_keyPairExported = true;
}

public StrongNameKeyPair (string keyPairContainer)
{
if (keyPairContainer == null) {
throw new ArgumentNullException ("keyPairContainer");
}

_keyPairContainer = keyPairContainer;
_keyPairExported = false;
}

protected StrongNameKeyPair (SerializationInfo info, StreamingContext context)
{
_keyPairExported = (bool) info.GetValue ("_keyPairExported", typeof(bool));
_keyPairArray = (byte []) info.GetValue ("_keyPairArray", typeof(byte []));
_keyPairContainer = (string) info.GetValue ("_keyPairContainer", typeof(string));
_publicKey = (byte []) info.GetValue ("_publicKey", typeof(byte []));
}

byte [] ComputePublicKey ()
{
using (var rsa = Mixin.CreateRSA (this)) {
var cspBlob = ToCapiPublicKeyBlob (rsa);
var publicKey = new byte[12 + cspBlob.Length];
Buffer.BlockCopy (cspBlob, 0, publicKey, 12, cspBlob.Length);
// The first 12 bytes are documented at:
// http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp
// ALG_ID - Signature
publicKey [1] = 36;
// ALG_ID - Hash
publicKey [4] = 4;
publicKey [5] = 128;
// Length of Public Key (in bytes)
publicKey [8] = (byte) (cspBlob.Length >> 0);
publicKey [9] = (byte) (cspBlob.Length >> 8);
publicKey [10] = (byte) (cspBlob.Length >> 16);
publicKey [11] = (byte) (cspBlob.Length >> 24);
return publicKey;
}
}

void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
{
info.AddValue ("_keyPairExported", _keyPairExported);
info.AddValue ("_keyPairArray", _keyPairArray);
info.AddValue ("_keyPairContainer", _keyPairContainer);
info.AddValue ("_publicKey", _publicKey);
}

static byte [] ToCapiPublicKeyBlob (RSA rsa)
{
var rsap = rsa.ExportParameters (false);
var blob = new byte[rsap.Modulus.Length + 20];
blob [0] = 0x06; // PUBLICKEYBLOB (0x06)
blob [1] = 0x02; // Version (0x02)
blob [2] = 0x00; // Reserved (word)
blob [3] = 0x00;
blob [5] = 0x24; // ALGID
WriteUInt32LE (blob, 8, 0x31415352); // DWORD magic = RSA1
WriteUInt32LE (blob, 12, (uint) rsap.Modulus.Length << 3); // DWORD bitlen

// DWORD public exponent
blob [18] = rsap.Exponent [0];
blob [17] = rsap.Exponent [1];
blob [16] = rsap.Exponent [2];

// BYTE modulus[rsapubkey.bitlen/8];
Array.Reverse (rsap.Modulus);
Buffer.BlockCopy (rsap.Modulus, 0, blob, 20, rsap.Modulus.Length);
return blob;
}

static void WriteUInt32LE (byte [] bytes, int offset, uint value)
{
bytes [offset + 3] = (byte) (value >> 24);
bytes [offset + 2] = (byte) (value >> 16);
bytes [offset + 1] = (byte) (value >> 8);
bytes [offset] = (byte) value;
}
}
}
#endif
20 changes: 19 additions & 1 deletion Mono.Security.Cryptography/CryptoService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

using System;
using System.IO;
using System.Reflection;
#if (NETSTANDARD)
using StrongNameKeyPair=Mono.Security.Cryptography.StrongNameKeyPair;
#else
using StrongNameKeyPair=System.Reflection.StrongNameKeyPair;
#endif
using System.Security.Cryptography;
using System.Runtime.Serialization;

Expand Down Expand Up @@ -191,6 +195,20 @@ public static RSA CreateRSA (this WriterParameters writer_parameters)
return new RSACryptoServiceProvider (parameters);
}

public static RSA CreateRSA (this StrongNameKeyPair key_pair)
{
if (!TryGetKeyContainer (key_pair, out var key, out var key_container))
return CryptoConvert.FromCapiKeyBlob (key);

var parameters = new CspParameters {
Flags = CspProviderFlags.UseMachineKeyStore,
KeyContainerName = key_container,
KeyNumber = 2,
};

return new RSACryptoServiceProvider (parameters);
}

static bool TryGetKeyContainer (ISerializable key_pair, out byte [] key, out string key_container)
{
var info = new SerializationInfo (typeof (StrongNameKeyPair), new FormatterConverter ());
Expand Down