Skip to content

Commit b749d80

Browse files
authored
Implement support for downloading ECDsa certificates (Azure#19194)
Resolves Azure#19112
1 parent 93fd3a1 commit b749d80

File tree

47 files changed

+23902
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+23902
-53
lines changed

sdk/core/Azure.Core/src/Shared/LightweightPkcs8Decoder.cs

Lines changed: 185 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.IO;
66
using System.Linq;
77
using System.Security.Cryptography;
8+
using System.Text;
89

910
namespace Azure.Core
1011
{
@@ -20,7 +21,7 @@ namespace Azure.Core
2021
///
2122
/// This code is able to decode RSA keys (without any attributes) from well formed PKCS#8 blobs.
2223
/// </summary>
23-
internal class LightweightPkcs8Decoder
24+
internal static partial class LightweightPkcs8Decoder
2425
{
2526
private static readonly byte[] s_derIntegerZero = { 0x02, 0x01, 0x00 };
2627

@@ -31,6 +32,159 @@ internal class LightweightPkcs8Decoder
3132
0x05, 0x00,
3233
};
3334

35+
internal static byte[] ReadBitString(byte[] data, ref int offset)
36+
{
37+
// Adapted from https://github.com/dotnet/runtime/blob/be74b4bd/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs#L156
38+
39+
if (data[offset++] != 0x03)
40+
{
41+
throw new InvalidDataException("Invalid PKCS#8 Data");
42+
}
43+
44+
int length = ReadLength(data, ref offset);
45+
if (length == 0)
46+
{
47+
throw new InvalidDataException("Invalid PKCS#8 Data");
48+
}
49+
50+
int unusedBitCount = data[offset++];
51+
if (unusedBitCount > 7)
52+
{
53+
throw new InvalidDataException("Invalid PKCS#8 Data");
54+
}
55+
56+
Span<byte> span = data.AsSpan(offset, length - 1);
57+
58+
// Build a mask for the bits that are used so the normalized value can be computed
59+
//
60+
// If 3 bits are "unused" then build a mask for them to check for 0.
61+
// -1 << 3 => 0b1111_1111 << 3 => 0b1111_1000
62+
int mask = -1 << unusedBitCount;
63+
byte lastByte = span[span.Length - 1];
64+
byte maskedByte = (byte)(lastByte & mask);
65+
66+
byte[] ret = new byte[span.Length];
67+
68+
Buffer.BlockCopy(data, offset, ret, 0, span.Length);
69+
ret[span.Length - 1] = maskedByte;
70+
71+
offset += span.Length;
72+
73+
return ret;
74+
}
75+
76+
internal static string ReadObjectIdentifier(byte[] data, ref int offset)
77+
{
78+
// Adapted from https://github.com/dotnet/runtime/blob/be74b4bd/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs#L175
79+
80+
if (data[offset++] != 0x06)
81+
{
82+
throw new InvalidDataException("Invalid PKCS#8 Data");
83+
}
84+
85+
int length = ReadLength(data, ref offset);
86+
87+
StringBuilder ret = new StringBuilder();
88+
for (int i = offset; i < offset + length; i++)
89+
{
90+
byte val = data[i];
91+
92+
if (i == offset)
93+
{
94+
byte first;
95+
if (val < 40)
96+
{
97+
first = 0;
98+
}
99+
else if (val < 80)
100+
{
101+
first = 1;
102+
val -= 40;
103+
}
104+
else
105+
{
106+
throw new InvalidDataException("Unsupported PKCS#8 Data");
107+
}
108+
109+
ret.Append(first).Append('.').Append(val);
110+
}
111+
else
112+
{
113+
if (val < 128)
114+
{
115+
ret.Append('.').Append(val);
116+
}
117+
else
118+
{
119+
ret.Append('.');
120+
121+
if (val == 0x80)
122+
{
123+
throw new InvalidDataException("Invalid PKCS#8 Data");
124+
}
125+
126+
// See how long the segment is.
127+
int end = -1;
128+
int idx;
129+
130+
for (idx = i; idx < offset + length; idx++)
131+
{
132+
if ((data[idx] & 0x80) == 0)
133+
{
134+
end = idx;
135+
break;
136+
}
137+
}
138+
139+
if (end < 0)
140+
{
141+
throw new InvalidDataException("Invalid PKCS#8 Data");
142+
}
143+
144+
// 4 or fewer bytes fits into a signed integer.
145+
int max = end + 1;
146+
if (max <= i + 4)
147+
{
148+
int accum = 0;
149+
for (idx = i; idx < max; idx++)
150+
{
151+
val = data[idx];
152+
accum <<= 7;
153+
accum |= (byte)(val & 0x7f);
154+
}
155+
156+
ret.Append(accum);
157+
i = end;
158+
}
159+
else
160+
{
161+
throw new InvalidDataException("Unsupported PKCS#8 Data");
162+
}
163+
}
164+
}
165+
}
166+
167+
offset += length;
168+
return ret.ToString();
169+
}
170+
171+
internal static byte[] ReadOctetString(byte[] data, ref int offset)
172+
{
173+
if (data[offset++] != 0x04)
174+
{
175+
throw new InvalidDataException("Invalid PKCS#8 Data");
176+
}
177+
178+
int length = ReadLength(data, ref offset);
179+
180+
byte[] ret = new byte[length];
181+
182+
Buffer.BlockCopy(data, offset, ret, 0, length);
183+
offset += length;
184+
185+
return ret;
186+
}
187+
34188
private static int ReadLength(byte[] data, ref int offset)
35189
{
36190
byte lengthOrLengthLength = data[offset++];
@@ -45,13 +199,13 @@ private static int ReadLength(byte[] data, ref int offset)
45199

46200
for (int i = 0; i < lengthLength; i++)
47201
{
202+
length <<= 8;
203+
length |= data[offset++];
204+
48205
if (length > ushort.MaxValue)
49206
{
50207
throw new InvalidDataException("Invalid PKCS#8 Data");
51208
}
52-
53-
length <<= 8;
54-
length |= data[offset++];
55209
}
56210

57211
return length;
@@ -107,6 +261,16 @@ private static byte[] ReadUnsignedInteger(byte[] data, ref int offset, int targe
107261
return ret;
108262
}
109263

264+
private static int ReadPayloadTagLength(byte[] data, ref int offset, byte tagValue)
265+
{
266+
if (data[offset++] != tagValue)
267+
{
268+
throw new InvalidDataException("Invalid PKCS#8 Data");
269+
}
270+
271+
return ReadLength(data, ref offset);
272+
}
273+
110274
private static void ConsumeFullPayloadTag(byte[] data, ref int offset, byte tagValue)
111275
{
112276
if (data[offset++] != tagValue)
@@ -173,5 +337,22 @@ public static RSA DecodeRSAPkcs8(byte[] pkcs8Bytes)
173337
rsa.ImportParameters(rsaParameters);
174338
return rsa;
175339
}
340+
341+
public static string DecodePrivateKeyOid(byte[] pkcs8Bytes)
342+
{
343+
int offset = 0;
344+
345+
// PrivateKeyInfo SEQUENCE
346+
ConsumeFullPayloadTag(pkcs8Bytes, ref offset, 0x30);
347+
348+
// PKCS#8 PrivateKeyInfo.version == 0
349+
ConsumeMatch(pkcs8Bytes, ref offset, s_derIntegerZero);
350+
351+
// PKCS#8 PrivateKeyInfo.sequence
352+
ReadPayloadTagLength(pkcs8Bytes, ref offset, 0x30);
353+
354+
// Return the AlgorithmIdentifier value
355+
return ReadObjectIdentifier(pkcs8Bytes, ref offset);
356+
}
176357
}
177358
}

0 commit comments

Comments
 (0)