Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 5b51318

Browse files
authored
Make EnvelopedCms work for Linux and macOS
This change adds a version of EnvelopedCms which uses the platform crypto stack and new AsnSerializer capabilities, and makes no P/Invokes (other than the ones the crypto stack itself makes). .NET Framework EnvelopedCms is more or less RFC 2630 (CMS version "2") plus RFC 3565 (AES with CMS), and that is maintained with this implementation. No new API has been added, this merely populates the existing API for non-Windows OSes. Unlike the Windows implementation, FFC based Diffie-Hellman key agreement is not supported, because the platform has no exposure of FFC Diffie-Hellman. Unlike the Windows implementation, RC4 is not supported, because the platform has no exposure of RC4.
1 parent d8d32ab commit 5b51318

Some content is hidden

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

41 files changed

+1921
-293
lines changed

src/Common/src/System/Security/Cryptography/Asn1V2.Serializer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,11 @@ private static void SerializeCustomType(Type typeT, object value, AsnWriter writ
351351
writer.PopSequence(tag);
352352
}
353353

354-
private static object DeserializeCustomType(AsnReader reader, Type typeT)
354+
private static object DeserializeCustomType(AsnReader reader, Type typeT, Asn1Tag expectedTag)
355355
{
356356
object target = Activator.CreateInstance(typeT);
357357

358-
AsnReader sequence = reader.ReadSequence();
358+
AsnReader sequence = reader.ReadSequence(expectedTag);
359359

360360
foreach (FieldInfo fieldInfo in typeT.GetFields(FieldFlags))
361361
{
@@ -1023,7 +1023,7 @@ private static Deserializer GetSimpleDeserializer(
10231023
{
10241024
if (fieldData.TagType == UniversalTagNumber.Sequence)
10251025
{
1026-
return reader => DeserializeCustomType(reader, typeT);
1026+
return reader => DeserializeCustomType(reader, typeT, expectedTag);
10271027
}
10281028
}
10291029

src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Helpers.cs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Buffers;
77
using System.Text;
88
using System.Diagnostics;
9+
using System.IO;
910
using System.Runtime.InteropServices;
1011
using System.Security.Cryptography;
1112
using System.Security.Cryptography.Asn1;
@@ -135,6 +136,26 @@ public static T[] NormalizeSet<T>(
135136
return set.SetData;
136137
}
137138

139+
internal static byte[] EncodeContentInfo<T>(
140+
T value,
141+
string contentType,
142+
AsnEncodingRules ruleSet = AsnEncodingRules.DER)
143+
{
144+
using (AsnWriter innerWriter = AsnSerializer.Serialize(value, ruleSet))
145+
{
146+
ContentInfoAsn content = new ContentInfoAsn
147+
{
148+
ContentType = contentType,
149+
Content = innerWriter.Encode(),
150+
};
151+
152+
using (AsnWriter outerWriter = AsnSerializer.Serialize(content, ruleSet))
153+
{
154+
return outerWriter.Encode();
155+
}
156+
}
157+
}
158+
138159
public static CmsRecipientCollection DeepCopy(this CmsRecipientCollection recipients)
139160
{
140161
CmsRecipientCollection recipientsCopy = new CmsRecipientCollection();
@@ -167,7 +188,7 @@ public static string OctetStringToUnicode(this byte[] octets)
167188

168189
public static X509Certificate2Collection GetStoreCertificates(StoreName storeName, StoreLocation storeLocation, bool openExistingOnly)
169190
{
170-
using (X509Store store = new X509Store())
191+
using (X509Store store = new X509Store(storeName, storeLocation))
171192
{
172193
OpenFlags flags = OpenFlags.ReadOnly | OpenFlags.IncludeArchived;
173194
if (openExistingOnly)
@@ -250,14 +271,14 @@ private static byte[] ToSkiBytes(this string skiString)
250271
return skiString.UpperHexStringToByteArray();
251272
}
252273

253-
public static string ToSkiString(this ReadOnlySpan<byte> skiBytes)
274+
public static string ToSkiString(this byte[] skiBytes)
254275
{
255276
return ToUpperHexString(skiBytes);
256277
}
257278

258-
public static string ToSkiString(this byte[] skiBytes)
279+
public static string ToBigEndianHex(this ReadOnlySpan<byte> bytes)
259280
{
260-
return ToUpperHexString(skiBytes);
281+
return ToUpperHexString(bytes);
261282
}
262283

263284
/// <summary>
@@ -414,6 +435,29 @@ internal static void DigestWriter(IncrementalHash hasher, AsnWriter writer)
414435
#endif
415436
}
416437

438+
internal static byte[] OneShot(this ICryptoTransform transform, byte[] data)
439+
{
440+
return OneShot(transform, data, 0, data.Length);
441+
}
442+
443+
internal static byte[] OneShot(this ICryptoTransform transform, byte[] data, int offset, int length)
444+
{
445+
if (transform.CanTransformMultipleBlocks)
446+
{
447+
return transform.TransformFinalBlock(data, offset, length);
448+
}
449+
450+
using (MemoryStream memoryStream = new MemoryStream())
451+
{
452+
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
453+
{
454+
cryptoStream.Write(data, offset, length);
455+
}
456+
457+
return memoryStream.ToArray();
458+
}
459+
}
460+
417461
private static ReadOnlyMemory<byte> GetSubjectPublicKeyInfo(X509Certificate2 certificate)
418462
{
419463
var parsedCertificate = AsnSerializer.Deserialize<Certificate>(certificate.RawData, AsnEncodingRules.DER);

src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Oids.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ namespace Internal.Cryptography
77
internal static class Oids
88
{
99
// Symmetric encryption algorithms
10+
public const string Rc2Cbc = "1.2.840.113549.3.2";
11+
public const string Rc4 = "1.2.840.113549.3.4";
1012
public const string TripleDesCbc = "1.2.840.113549.3.7";
13+
public const string DesCbc = "1.3.14.3.2.7";
14+
public const string Aes128Cbc = "2.16.840.1.101.3.4.1.2";
15+
public const string Aes192Cbc = "2.16.840.1.101.3.4.1.22";
16+
public const string Aes256Cbc = "2.16.840.1.101.3.4.1.42";
1117

1218
// Asymmetric encryption algorithms
1319
public const string Rsa = "1.2.840.113549.1.1.1";
20+
public const string RsaOaep = "1.2.840.113549.1.1.7";
1421
public const string RsaPss = "1.2.840.113549.1.1.10";
1522
public const string Esdh = "1.2.840.113549.1.9.16.3.5";
1623

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Diagnostics;
7+
using System.Security.Cryptography;
8+
using System.Security.Cryptography.Asn1;
9+
using System.Security.Cryptography.Pkcs;
10+
using System.Security.Cryptography.Pkcs.Asn1;
11+
using System.Security.Cryptography.X509Certificates;
12+
using System.Security.Cryptography.Xml;
13+
14+
namespace Internal.Cryptography.Pal.AnyOS
15+
{
16+
internal static class AsnHelpers
17+
{
18+
internal static SubjectIdentifierOrKey ToSubjectIdentifierOrKey(
19+
this OriginatorIdentifierOrKeyAsn originator)
20+
{
21+
if (originator.IssuerAndSerialNumber != null)
22+
{
23+
var name = new X500DistinguishedName(originator.IssuerAndSerialNumber.Value.Issuer.ToArray());
24+
25+
return new SubjectIdentifierOrKey(
26+
SubjectIdentifierOrKeyType.IssuerAndSerialNumber,
27+
new X509IssuerSerial(
28+
name.Name,
29+
originator.IssuerAndSerialNumber.Value.SerialNumber.Span.ToBigEndianHex()));
30+
}
31+
32+
if (originator.SubjectKeyIdentifier != null)
33+
{
34+
return new SubjectIdentifierOrKey(
35+
SubjectIdentifierOrKeyType.SubjectKeyIdentifier,
36+
originator.SubjectKeyIdentifier.Value.Span.ToBigEndianHex());
37+
}
38+
39+
if (originator.OriginatorKey != null)
40+
{
41+
OriginatorPublicKeyAsn originatorKey = originator.OriginatorKey;
42+
43+
return new SubjectIdentifierOrKey(
44+
SubjectIdentifierOrKeyType.PublicKeyInfo,
45+
new PublicKeyInfo(
46+
originatorKey.Algorithm.ToPresentationObject(),
47+
originatorKey.PublicKey.ToArray()));
48+
}
49+
50+
Debug.Fail("Unknown SubjectIdentifierOrKey state");
51+
return new SubjectIdentifierOrKey(SubjectIdentifierOrKeyType.Unknown, String.Empty);
52+
}
53+
54+
internal static AlgorithmIdentifier ToPresentationObject(this AlgorithmIdentifierAsn asn)
55+
{
56+
int keyLength;
57+
58+
switch (asn.Algorithm.Value)
59+
{
60+
case Oids.Rc2Cbc:
61+
{
62+
if (asn.Parameters == null)
63+
{
64+
keyLength = 0;
65+
break;
66+
}
67+
68+
Rc2CbcParameters rc2Params = AsnSerializer.Deserialize<Rc2CbcParameters>(
69+
asn.Parameters.Value,
70+
AsnEncodingRules.BER);
71+
72+
int keySize = rc2Params.GetEffectiveKeyBits();
73+
74+
// These are the only values .NET Framework would set.
75+
switch (keySize)
76+
{
77+
case 40:
78+
case 56:
79+
case 64:
80+
case 128:
81+
keyLength = keySize;
82+
break;
83+
default:
84+
keyLength = 0;
85+
break;
86+
}
87+
88+
break;
89+
}
90+
case Oids.Rc4:
91+
{
92+
if (asn.Parameters == null)
93+
{
94+
keyLength = 0;
95+
break;
96+
}
97+
98+
int saltLen = 0;
99+
AsnReader reader = new AsnReader(asn.Parameters.Value, AsnEncodingRules.BER);
100+
101+
// DER NULL is considered the same as not present.
102+
// No call to ReadNull() is necessary because the serializer already verified that
103+
// there's no data after the [AnyValue] value.
104+
if (reader.PeekTag() != Asn1Tag.Null)
105+
{
106+
if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory<byte> contents))
107+
{
108+
saltLen = contents.Length;
109+
}
110+
else
111+
{
112+
Span<byte> salt = stackalloc byte[KeyLengths.Rc4Max_128Bit / 8];
113+
114+
if (!reader.TryCopyOctetStringBytes(salt, out saltLen))
115+
{
116+
throw new CryptographicException();
117+
}
118+
}
119+
}
120+
121+
keyLength = KeyLengths.Rc4Max_128Bit - 8 * saltLen;
122+
break;
123+
}
124+
case Oids.DesCbc:
125+
keyLength = KeyLengths.Des_64Bit;
126+
break;
127+
case Oids.TripleDesCbc:
128+
keyLength = KeyLengths.TripleDes_192Bit;
129+
break;
130+
default:
131+
// .NET Framework doesn't set a keylength for AES, or any other algorithm than the ones
132+
// listed here.
133+
keyLength = 0;
134+
break;
135+
}
136+
137+
return new AlgorithmIdentifier(new Oid(asn.Algorithm), keyLength);
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)