8cb55d97b6aabeee9daad68cbe513ff2a12cf02b
[platform/upstream/dotnet/runtime.git] /
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
4 using Internal.Cryptography;
5 using System.IO;
6 using System.Runtime.Versioning;
7
8 namespace System.Security.Cryptography
9 {
10     public sealed partial class RSACryptoServiceProvider : RSA, ICspAsymmetricAlgorithm, IRuntimeAlgorithm
11     {
12         private const int DefaultKeySize = 1024;
13
14         private readonly RSA _impl;
15         private bool _publicOnly;
16
17         [UnsupportedOSPlatform("browser")]
18         public RSACryptoServiceProvider()
19             : this(DefaultKeySize) { }
20
21         [UnsupportedOSPlatform("browser")]
22         public RSACryptoServiceProvider(int dwKeySize)
23         {
24             ArgumentOutOfRangeException.ThrowIfNegative(dwKeySize);
25
26             // This class wraps RSA
27             _impl = RSA.Create(dwKeySize);
28         }
29
30         [SupportedOSPlatform("windows")]
31         public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters) =>
32             throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CAPI_Required, nameof(CspParameters)));
33
34         [SupportedOSPlatform("windows")]
35         public RSACryptoServiceProvider(CspParameters parameters) =>
36             throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CAPI_Required, nameof(CspParameters)));
37
38         [SupportedOSPlatform("windows")]
39         public CspKeyContainerInfo CspKeyContainerInfo =>
40             throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CAPI_Required, nameof(CspKeyContainerInfo)));
41
42         public byte[] Decrypt(byte[] rgb, bool fOAEP)
43         {
44             ArgumentNullException.ThrowIfNull(rgb);
45
46             // size check -- must be exactly the modulus size
47             if (rgb.Length != (KeySize / 8))
48                 throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize);
49
50             return _impl.Decrypt(rgb, fOAEP ? RSAEncryptionPadding.OaepSHA1 : RSAEncryptionPadding.Pkcs1);
51         }
52
53         public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
54         {
55             ArgumentNullException.ThrowIfNull(data);
56             ArgumentNullException.ThrowIfNull(padding);
57
58             return
59                 padding == RSAEncryptionPadding.Pkcs1 ? Decrypt(data, fOAEP: false) :
60                 padding == RSAEncryptionPadding.OaepSHA1 ? Decrypt(data, fOAEP: true) : // For compat, this prevents OaepSHA2 options as fOAEP==true will cause Decrypt to use OaepSHA1
61                 throw PaddingModeNotSupported();
62         }
63
64         public override bool TryDecrypt(ReadOnlySpan<byte> data, Span<byte> destination, RSAEncryptionPadding padding, out int bytesWritten)
65         {
66             ArgumentNullException.ThrowIfNull(padding);
67
68             if (data.Length != (KeySize / 8))
69                 throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize);
70             if (padding != RSAEncryptionPadding.Pkcs1 && padding != RSAEncryptionPadding.OaepSHA1)
71                 throw PaddingModeNotSupported();
72
73             return _impl.TryDecrypt(data, destination, padding, out bytesWritten);
74         }
75
76         protected override void Dispose(bool disposing)
77         {
78             if (disposing)
79             {
80                 _impl.Dispose();
81                 base.Dispose(disposing);
82             }
83         }
84
85         public byte[] Encrypt(byte[] rgb, bool fOAEP)
86         {
87             ArgumentNullException.ThrowIfNull(rgb);
88
89             return _impl.Encrypt(rgb, fOAEP ? RSAEncryptionPadding.OaepSHA1 : RSAEncryptionPadding.Pkcs1);
90         }
91
92         public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
93         {
94             ArgumentNullException.ThrowIfNull(data);
95             ArgumentNullException.ThrowIfNull(padding);
96
97             return
98                 padding == RSAEncryptionPadding.Pkcs1 ? Encrypt(data, fOAEP: false) :
99                 padding == RSAEncryptionPadding.OaepSHA1 ?  Encrypt(data, fOAEP: true) : // For compat, this prevents OaepSHA2 options as fOAEP==true will cause Decrypt to use OaepSHA1
100                 throw PaddingModeNotSupported();
101         }
102
103         public override bool TryEncrypt(ReadOnlySpan<byte> data, Span<byte> destination, RSAEncryptionPadding padding, out int bytesWritten)
104         {
105             ArgumentNullException.ThrowIfNull(padding);
106
107             if (padding != RSAEncryptionPadding.Pkcs1 && padding != RSAEncryptionPadding.OaepSHA1)
108                 throw PaddingModeNotSupported();
109
110             return _impl.TryEncrypt(data, destination, padding, out bytesWritten);
111         }
112
113         public byte[] ExportCspBlob(bool includePrivateParameters)
114         {
115             RSAParameters parameters = ExportParameters(includePrivateParameters);
116             return parameters.ToKeyBlob();
117         }
118
119         public override RSAParameters ExportParameters(bool includePrivateParameters) =>
120             _impl.ExportParameters(includePrivateParameters);
121
122         public override void FromXmlString(string xmlString) => _impl.FromXmlString(xmlString);
123
124         public void ImportCspBlob(byte[] keyBlob)
125         {
126             RSAParameters parameters = CapiHelper.ToRSAParameters(keyBlob, !IsPublic(keyBlob));
127             ImportParameters(parameters);
128         }
129
130         public override void ImportParameters(RSAParameters parameters)
131         {
132             // Although _impl supports larger Exponent, limit here for compat.
133             if (parameters.Exponent == null || parameters.Exponent.Length > 4)
134                 throw new CryptographicException(SR.Argument_InvalidValue);
135
136             _impl.ImportParameters(parameters);
137
138             // P was verified in ImportParameters
139             _publicOnly = (parameters.P == null || parameters.P.Length == 0);
140         }
141
142         public override void ImportEncryptedPkcs8PrivateKey(
143             ReadOnlySpan<byte> passwordBytes,
144             ReadOnlySpan<byte> source,
145             out int bytesRead)
146         {
147             _impl.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
148         }
149
150         public override void ImportEncryptedPkcs8PrivateKey(
151             ReadOnlySpan<char> password,
152             ReadOnlySpan<byte> source,
153             out int bytesRead)
154         {
155             _impl.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
156         }
157
158         public override string? KeyExchangeAlgorithm => _impl.KeyExchangeAlgorithm;
159
160         public override int KeySize
161         {
162             get { return _impl.KeySize; }
163             set { _impl.KeySize = value; }
164         }
165
166         // RSAOpenSsl is (512, 16384, 8), RSASecurityTransforms is (1024, 16384, 8)
167         // Either way the minimum is lifted off of CAPI's 384, due to platform constraints.
168         public override KeySizes[] LegalKeySizes => _impl.LegalKeySizes;
169
170         // PersistKeyInCsp has no effect in Unix
171         public bool PersistKeyInCsp { get; set; }
172
173         public bool PublicOnly => _publicOnly;
174
175         public override string SignatureAlgorithm => "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
176
177         public override byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
178         {
179             ArgumentNullException.ThrowIfNull(padding);
180
181             if (padding != RSASignaturePadding.Pkcs1)
182             {
183                 throw PaddingModeNotSupported();
184             }
185
186             return _impl.SignData(data, hashAlgorithm, padding);
187         }
188
189         public override byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
190         {
191             ArgumentNullException.ThrowIfNull(padding);
192
193             if (padding != RSASignaturePadding.Pkcs1)
194             {
195                 throw PaddingModeNotSupported();
196             }
197
198             return _impl.SignData(data, offset, count, hashAlgorithm, padding);
199         }
200
201         public override bool TrySignData(ReadOnlySpan<byte> data, Span<byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, out int bytesWritten)
202         {
203             ArgumentNullException.ThrowIfNull(padding);
204
205             if (padding != RSASignaturePadding.Pkcs1)
206             {
207                 throw PaddingModeNotSupported();
208             }
209
210             return _impl.TrySignData(data, destination, hashAlgorithm, padding, out bytesWritten);
211         }
212
213         public byte[] SignData(byte[] buffer, int offset, int count, object halg) =>
214             _impl.SignData(buffer, offset, count, CapiHelper.ObjToHashAlgorithmName(halg), RSASignaturePadding.Pkcs1);
215
216         public byte[] SignData(byte[] buffer, object halg) =>
217             _impl.SignData(buffer, CapiHelper.ObjToHashAlgorithmName(halg), RSASignaturePadding.Pkcs1);
218
219         public byte[] SignData(Stream inputStream, object halg) =>
220             _impl.SignData(inputStream, CapiHelper.ObjToHashAlgorithmName(halg), RSASignaturePadding.Pkcs1);
221
222         public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
223         {
224             ArgumentNullException.ThrowIfNull(padding);
225
226             if (padding != RSASignaturePadding.Pkcs1)
227             {
228                 throw PaddingModeNotSupported();
229             }
230
231             return _impl.SignHash(hash, hashAlgorithm, padding);
232         }
233
234         public override bool TrySignHash(ReadOnlySpan<byte> hash, Span<byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, out int bytesWritten)
235         {
236             ArgumentNullException.ThrowIfNull(padding);
237
238             if (padding != RSASignaturePadding.Pkcs1)
239             {
240                 throw PaddingModeNotSupported();
241             }
242
243             return _impl.TrySignHash(hash, destination, hashAlgorithm, padding, out bytesWritten);
244         }
245
246         public byte[] SignHash(byte[] rgbHash, string str)
247         {
248             ArgumentNullException.ThrowIfNull(rgbHash);
249
250             if (PublicOnly)
251                 throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
252
253             HashAlgorithmName algName = CapiHelper.NameOrOidToHashAlgorithmName(str);
254             return _impl.SignHash(rgbHash, algName, RSASignaturePadding.Pkcs1);
255         }
256
257         public override string ToXmlString(bool includePrivateParameters) => _impl.ToXmlString(includePrivateParameters);
258
259         public bool VerifyData(byte[] buffer, object halg, byte[] signature) =>
260             _impl.VerifyData(buffer, signature, CapiHelper.ObjToHashAlgorithmName(halg), RSASignaturePadding.Pkcs1);
261
262         public override bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
263         {
264             ArgumentNullException.ThrowIfNull(padding);
265
266             if (padding != RSASignaturePadding.Pkcs1)
267             {
268                 throw PaddingModeNotSupported();
269             }
270
271             return _impl.VerifyData(data, offset, count, signature, hashAlgorithm, padding);
272         }
273
274         public override bool VerifyData(ReadOnlySpan<byte> data, ReadOnlySpan<byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
275         {
276             ArgumentNullException.ThrowIfNull(padding);
277
278             if (padding != RSASignaturePadding.Pkcs1)
279             {
280                 throw PaddingModeNotSupported();
281             }
282
283             return _impl.VerifyData(data, signature, hashAlgorithm, padding);
284         }
285
286         public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
287         {
288             ArgumentNullException.ThrowIfNull(hash);
289             ArgumentNullException.ThrowIfNull(signature);
290
291             return VerifyHash((ReadOnlySpan<byte>)hash, (ReadOnlySpan<byte>)signature, hashAlgorithm, padding);
292         }
293
294         public override bool VerifyHash(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
295         {
296             ArgumentNullException.ThrowIfNull(padding);
297
298             if (padding != RSASignaturePadding.Pkcs1)
299             {
300                 throw PaddingModeNotSupported();
301             }
302
303             return _impl.VerifyHash(hash, signature, hashAlgorithm, padding);
304         }
305
306         public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature)
307         {
308             ArgumentNullException.ThrowIfNull(rgbHash);
309             ArgumentNullException.ThrowIfNull(rgbSignature);
310
311             return VerifyHash(
312                 (ReadOnlySpan<byte>)rgbHash, (ReadOnlySpan<byte>)rgbSignature,
313                 CapiHelper.NameOrOidToHashAlgorithmName(str), RSASignaturePadding.Pkcs1);
314         }
315
316         // UseMachineKeyStore has no effect in Unix
317         public static bool UseMachineKeyStore { get; set; }
318
319         private static Exception PaddingModeNotSupported()
320         {
321             return new CryptographicException(SR.Cryptography_InvalidPaddingMode);
322         }
323
324         /// <summary>
325         /// find whether an RSA key blob is public.
326         /// </summary>
327         private static bool IsPublic(byte[] keyBlob)
328         {
329             ArgumentNullException.ThrowIfNull(keyBlob);
330
331             // The CAPI RSA public key representation consists of the following sequence:
332             //  - BLOBHEADER
333             //  - RSAPUBKEY
334
335             // The first should be PUBLICKEYBLOB and magic should be RSA_PUB_MAGIC "RSA1"
336             if (keyBlob[0] != CapiHelper.PUBLICKEYBLOB)
337             {
338                 return false;
339             }
340
341             if (keyBlob[11] != 0x31 || keyBlob[10] != 0x41 || keyBlob[9] != 0x53 || keyBlob[8] != 0x52)
342             {
343                 return false;
344             }
345
346             return true;
347         }
348     }
349 }