Skip to content

Commit eef0fa3

Browse files
committed
Fixed license validation for .NET Core under Linux and MacOS (#269)
1 parent ec3ccae commit eef0fa3

File tree

2 files changed

+112
-5
lines changed

2 files changed

+112
-5
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#if NETSTANDARD1_4
2+
namespace MyTested.AspNetCore.Mvc.Licensing
3+
{
4+
using System;
5+
using System.IO;
6+
using System.Security.Cryptography;
7+
8+
public static class CryptographyHelpers
9+
{
10+
private const int ALG_TYPE_RSA = (2 << 9);
11+
private const int ALG_CLASS_KEY_EXCHANGE = (5 << 13);
12+
private const int CALG_RSA_KEYX = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | 0);
13+
14+
internal static RSAParameters ToRSAParameters(this byte[] cspBlob, bool includePrivateParameters)
15+
{
16+
BinaryReader br = new BinaryReader(new MemoryStream(cspBlob));
17+
18+
byte bType = br.ReadByte(); // BLOBHEADER.bType: Expected to be 0x6 (PUBLICKEYBLOB) or 0x7 (PRIVATEKEYBLOB), though there's no check for backward compat reasons.
19+
byte bVersion = br.ReadByte(); // BLOBHEADER.bVersion: Expected to be 0x2, though there's no check for backward compat reasons.
20+
br.ReadUInt16(); // BLOBHEADER.wReserved
21+
int algId = br.ReadInt32(); // BLOBHEADER.aiKeyAlg
22+
if (algId != CALG_RSA_KEYX)
23+
throw new PlatformNotSupportedException(); // The FCall this code was ported from supports other algid's but we're only porting what we use.
24+
25+
int magic = br.ReadInt32(); // RSAPubKey.magic: Expected to be 0x31415352 ('RSA1') or 0x32415352 ('RSA2')
26+
int bitLen = br.ReadInt32(); // RSAPubKey.bitLen
27+
28+
int modulusLength = bitLen / 8;
29+
int halfModulusLength = (modulusLength + 1) / 2;
30+
31+
uint expAsDword = br.ReadUInt32();
32+
33+
RSAParameters rsaParameters = new RSAParameters();
34+
rsaParameters.Exponent = ExponentAsBytes(expAsDword);
35+
rsaParameters.Modulus = br.ReadReversed(modulusLength);
36+
if (includePrivateParameters)
37+
{
38+
rsaParameters.P = br.ReadReversed(halfModulusLength);
39+
rsaParameters.Q = br.ReadReversed(halfModulusLength);
40+
rsaParameters.DP = br.ReadReversed(halfModulusLength);
41+
rsaParameters.DQ = br.ReadReversed(halfModulusLength);
42+
rsaParameters.InverseQ = br.ReadReversed(halfModulusLength);
43+
rsaParameters.D = br.ReadReversed(modulusLength);
44+
}
45+
46+
return rsaParameters;
47+
}
48+
49+
/// <summary>
50+
/// Helper for converting a UInt32 exponent to bytes.
51+
/// </summary>
52+
private static byte[] ExponentAsBytes(uint exponent)
53+
{
54+
if (exponent <= 0xFF)
55+
{
56+
return new[] { (byte)exponent };
57+
}
58+
if (exponent <= 0xFFFF)
59+
{
60+
return new[]
61+
{
62+
(byte) (exponent >> 8),
63+
(byte) (exponent)
64+
};
65+
}
66+
if (exponent <= 0xFFFFFF)
67+
{
68+
return new[]
69+
{
70+
(byte) (exponent >> 16),
71+
(byte) (exponent >> 8),
72+
(byte) (exponent)
73+
};
74+
}
75+
76+
return new[]
77+
{
78+
(byte) (exponent >> 24),
79+
(byte) (exponent >> 16),
80+
(byte) (exponent >> 8),
81+
(byte) (exponent)
82+
};
83+
}
84+
85+
/// <summary>
86+
/// Read in a byte array in reverse order.
87+
/// </summary>
88+
private static byte[] ReadReversed(this BinaryReader br, int count)
89+
{
90+
byte[] data = br.ReadBytes(count);
91+
Array.Reverse(data);
92+
return data;
93+
}
94+
}
95+
}
96+
#endif

src/MyTested.AspNetCore.Mvc.Licensing/LicenseValidator.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public static bool Validate(IEnumerable<string> licenses, DateTime releaseDate,
4040

4141
return true;
4242
}
43-
43+
4444
public static IEnumerable<LicenseDetails> GetLicenseDetails()
4545
{
4646
if (registeredLicenses == null || !registeredLicenses.Any())
@@ -135,18 +135,29 @@ private static void Validate(string license, DateTime releaseDate, string projec
135135
};
136136

137137
var parsedSigningData = licenseDetails.GetSignificateData();
138+
139+
var signingData = new byte[SigningDataLength];
140+
Array.Copy(licenseAsBytes, signingData, SigningDataLength);
138141

142+
#if NETSTANDARD1_4
143+
var cryptoProvider = RSA.Create();
144+
cryptoProvider.KeySize = 1024;
145+
146+
var parameters = CryptographyHelpers.ToRSAParameters(Convert.FromBase64String(PublicKey), false);
147+
cryptoProvider.ImportParameters(parameters);
148+
149+
var dataVerified = cryptoProvider.VerifyData(parsedSigningData, signingData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
150+
#else
139151
var cryptoProvider = new RSACryptoServiceProvider(1024)
140152
{
141153
PersistKeyInCsp = false
142154
};
143155

144156
cryptoProvider.ImportCspBlob(Convert.FromBase64String(PublicKey));
145157

146-
var signingData = new byte[SigningDataLength];
147-
Array.Copy(licenseAsBytes, signingData, SigningDataLength);
148-
149-
if (!cryptoProvider.VerifyData(parsedSigningData, SHA1.Create(), signingData))
158+
var dataVerified = cryptoProvider.VerifyData(parsedSigningData, SHA1.Create(), signingData);
159+
#endif
160+
if (!dataVerified)
150161
{
151162
throw new InvalidLicenseException("License text does not match signature");
152163
}

0 commit comments

Comments
 (0)