Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / rsa.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 //  RSA.cs
11 //
12
13 namespace System.Security.Cryptography {
14     using System.IO;
15     using System.Text;
16     using System.Runtime.Serialization;
17     using System.Security.Util;
18     using System.Globalization;
19     using System.Diagnostics.Contracts;
20
21     // We allow only the public components of an RSAParameters object, the Modulus and Exponent
22     // to be serializable.
23     [Serializable]
24 [System.Runtime.InteropServices.ComVisible(true)]
25     public struct RSAParameters {
26         public byte[]      Exponent;
27         public byte[]      Modulus;
28 #if SILVERLIGHT
29         public byte[] P;
30         public byte[] Q;
31         public byte[] DP;
32         public byte[] DQ;
33         public byte[] InverseQ;
34         public byte[] D;
35 #else // SILVERLIGHT
36         [NonSerialized] public byte[]      P;
37         [NonSerialized] public byte[]      Q;
38         [NonSerialized] public byte[]      DP;
39         [NonSerialized] public byte[]      DQ;
40         [NonSerialized] public byte[]      InverseQ;
41         [NonSerialized] public byte[]      D;
42 #endif // SILVERLIGHT
43     }
44
45 [System.Runtime.InteropServices.ComVisible(true)]
46     public abstract class RSA : AsymmetricAlgorithm
47     {
48         protected RSA() { }
49         //
50         // public methods
51         //
52
53         new static public RSA Create() {
54 #if FULL_AOT_RUNTIME
55             return new System.Security.Cryptography.RSACryptoServiceProvider ();
56 #else
57             return Create("System.Security.Cryptography.RSA");
58 #endif
59         }
60
61         new static public RSA Create(String algName) {
62             return (RSA) CryptoConfig.CreateFromName(algName);
63         }
64
65         //
66         // New RSA encrypt/decrypt/sign/verify RSA abstractions in .NET 4.6+ and .NET Core
67         //
68         // Methods that throw DerivedClassMustOverride are effectively abstract but we
69         // cannot mark them as such as it would be a breaking change. We'll make them
70         // abstract in .NET Core.
71
72         public virtual byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
73             throw DerivedClassMustOverride();
74         }
75
76         public virtual byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
77             throw DerivedClassMustOverride();
78         }
79
80         public virtual byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
81             throw DerivedClassMustOverride();
82         }
83
84         public virtual bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
85             throw DerivedClassMustOverride();
86         }
87
88         protected virtual byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
89             throw DerivedClassMustOverride();
90         }
91
92         protected virtual byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
93             throw DerivedClassMustOverride();
94         }
95
96         public byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
97             if (data == null) {
98                 throw new ArgumentNullException("data");
99             }
100             return SignData(data, 0, data.Length, hashAlgorithm, padding);
101         }
102
103         public virtual byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
104             if (data == null) {
105                 throw new ArgumentNullException("data");
106             }
107             if (offset < 0 || offset > data.Length) {
108                 throw new ArgumentOutOfRangeException("offset");
109             }
110             if (count < 0 || count > data.Length - offset) {
111                 throw new ArgumentOutOfRangeException("count");
112             }
113             if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
114                 throw HashAlgorithmNameNullOrEmpty();
115             }
116             if (padding == null) {
117                 throw new ArgumentNullException("padding");
118             }
119
120             byte[] hash = HashData(data, offset, count, hashAlgorithm);
121             return SignHash(hash, hashAlgorithm, padding);
122         }
123
124         public virtual byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
125             if (data == null) {
126                 throw new ArgumentNullException("data");
127             }
128             if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
129                 throw HashAlgorithmNameNullOrEmpty();
130             }
131             if (padding == null) {
132                 throw new ArgumentNullException("padding");
133             }
134     
135             byte[] hash = HashData(data, hashAlgorithm);
136             return SignHash(hash, hashAlgorithm, padding);
137         }
138
139         public bool VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
140             if (data == null) {
141                 throw new ArgumentNullException("data");
142             }
143             return VerifyData(data, 0, data.Length, signature, hashAlgorithm, padding);
144         }
145
146         public virtual bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
147             if (data == null) {
148                 throw new ArgumentNullException("data");
149             }
150             if (offset < 0 || offset > data.Length) {
151                 throw new ArgumentOutOfRangeException("offset");
152             }
153             if (count < 0 || count > data.Length - offset) {
154                 throw new ArgumentOutOfRangeException("count"); 
155             }
156             if (signature == null) {
157                 throw new ArgumentNullException("signature");
158             }
159             if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
160                 throw HashAlgorithmNameNullOrEmpty();
161             }
162             if (padding == null) {
163                 throw new ArgumentNullException("padding");
164             }
165
166             byte[] hash = HashData(data, offset, count, hashAlgorithm);
167             return VerifyHash(hash, signature, hashAlgorithm, padding);
168         }
169
170         public bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
171             if (data == null) {
172                 throw new ArgumentNullException("data");
173             }
174             if (signature == null) {
175                 throw new ArgumentNullException("signature");
176             }
177             if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
178                 throw HashAlgorithmNameNullOrEmpty();
179             }
180             if (padding == null) {
181                 throw new ArgumentNullException("padding");
182             }
183  
184             byte[] hash = HashData(data, hashAlgorithm);
185             return VerifyHash(hash, signature, hashAlgorithm, padding);
186         }
187
188         private static Exception DerivedClassMustOverride() {
189             return new NotImplementedException(Environment.GetResourceString("NotSupported_SubclassOverride"));
190         }
191
192         internal static Exception HashAlgorithmNameNullOrEmpty() {
193             return new ArgumentException(Environment.GetResourceString("Cryptography_HashAlgorithmNameNullOrEmpty"), "hashAlgorithm");
194         }
195
196         //
197         // Legacy encrypt/decrypt RSA abstraction from .NET < 4.6
198         //
199         // These should be obsolete, but we can't mark them as such here due to rules around not introducing
200         // source breaks to scenarios that compile against the GAC.
201         //
202         // They used to be abstract, but the only concrete implementation in RSACryptoServiceProvider threw
203         // NotSupportedException! This has been moved up to the base so all subclasses can ignore them moving forward.
204         // They will also be removed from .NET Core altogether.
205         //
206         // The original intent was for these to perform the RSA algorithm without padding/depadding. This can
207         // be seen by how the RSAXxx(De)Formatter classes call them in the non-RSACryptoServiceProvider case -- 
208         // they do the padding/depadding in managed code.
209         //
210         // Unfortunately, these formatter classes are still incompatible with RSACng or any derived class that does not
211         // implement EncryptValue, DecryptValue as the formatters speculatively expected non-RSACryptoServiceProvider
212         // to do. That needs to be fixed in a subsequent release. We can still do it as it would move an exception to a
213         // correct result...
214         //
215
216         // [Obsolete]
217         public virtual byte[] DecryptValue(byte[] rgb) {
218            throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
219         }
220
221         // [Obsolete]
222         public virtual byte[] EncryptValue(byte[] rgb) {
223            throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
224        }
225
226         //
227         // These should also be obsolete (on the base). They aren't well defined nor are they used
228         // anywhere in the FX apart from checking that they're not null.
229         //
230         // For new derived RSA classes, we'll just return "RSA" which is analagous to what ECDsa
231         // and ECDiffieHellman do.
232         //
233         // Note that for compat, RSACryptoServiceProvider still overrides and returns RSA-PKCS1-KEYEX 
234         // and http://www.w3.org/2000/09/xmldsig#rsa-sha1
235         //
236
237         public override string KeyExchangeAlgorithm {
238             get { return "RSA"; }
239         }
240
241         public override string SignatureAlgorithm {
242             get { return "RSA"; }
243         }
244
245
246         // Import/export functions
247
248         // We can provide a default implementation of FromXmlString because we require 
249         // every RSA implementation to implement ImportParameters
250         // All we have to do here is parse the XML.
251
252         public override void FromXmlString(String xmlString) {
253             if (xmlString == null) throw new ArgumentNullException("xmlString");
254             Contract.EndContractBlock();
255             RSAParameters rsaParams = new RSAParameters();
256             Parser p = new Parser(xmlString);
257             SecurityElement topElement = p.GetTopElement();
258
259             // Modulus is always present
260             String modulusString = topElement.SearchForTextOfLocalName("Modulus");
261             if (modulusString == null) {
262                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","RSA","Modulus"));
263             }
264             rsaParams.Modulus = Convert.FromBase64String(Utils.DiscardWhiteSpaces(modulusString));
265
266             // Exponent is always present
267             String exponentString = topElement.SearchForTextOfLocalName("Exponent");
268             if (exponentString == null) {
269                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","RSA","Exponent"));
270             }
271             rsaParams.Exponent = Convert.FromBase64String(Utils.DiscardWhiteSpaces(exponentString));
272
273             // P is optional
274             String pString = topElement.SearchForTextOfLocalName("P");
275             if (pString != null) rsaParams.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(pString));
276
277             // Q is optional
278             String qString = topElement.SearchForTextOfLocalName("Q");
279             if (qString != null) rsaParams.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(qString));
280
281             // DP is optional
282             String dpString = topElement.SearchForTextOfLocalName("DP");
283             if (dpString != null) rsaParams.DP = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dpString));
284
285             // DQ is optional
286             String dqString = topElement.SearchForTextOfLocalName("DQ");
287             if (dqString != null) rsaParams.DQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dqString));
288
289             // InverseQ is optional
290             String inverseQString = topElement.SearchForTextOfLocalName("InverseQ");
291             if (inverseQString != null) rsaParams.InverseQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inverseQString));
292
293             // D is optional
294             String dString = topElement.SearchForTextOfLocalName("D");
295             if (dString != null) rsaParams.D = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dString));
296
297             ImportParameters(rsaParams);
298         }
299
300         // We can provide a default implementation of ToXmlString because we require 
301         // every RSA implementation to implement ImportParameters
302         // If includePrivateParameters is false, this is just an XMLDSIG RSAKeyValue
303         // clause.  If includePrivateParameters is true, then we extend RSAKeyValue with 
304         // the other (private) elements.
305         public override String ToXmlString(bool includePrivateParameters) {
306             // From the XMLDSIG spec, RFC 3075, Section 6.4.2, an RSAKeyValue looks like this:
307             /* 
308                <element name="RSAKeyValue"> 
309                  <complexType> 
310                    <sequence>
311                      <element name="Modulus" type="ds:CryptoBinary"/> 
312                      <element name="Exponent" type="ds:CryptoBinary"/>
313                    </sequence> 
314                  </complexType> 
315                </element>
316             */
317             // we extend appropriately for private components
318             RSAParameters rsaParams = this.ExportParameters(includePrivateParameters);
319             StringBuilder sb = new StringBuilder();
320             sb.Append("<RSAKeyValue>");
321             // Add the modulus
322             sb.Append("<Modulus>"+Convert.ToBase64String(rsaParams.Modulus)+"</Modulus>");
323             // Add the exponent
324             sb.Append("<Exponent>"+Convert.ToBase64String(rsaParams.Exponent)+"</Exponent>");
325             if (includePrivateParameters) {
326                 // Add the private components
327                 sb.Append("<P>"+Convert.ToBase64String(rsaParams.P)+"</P>");
328                 sb.Append("<Q>"+Convert.ToBase64String(rsaParams.Q)+"</Q>");
329                 sb.Append("<DP>"+Convert.ToBase64String(rsaParams.DP)+"</DP>");
330                 sb.Append("<DQ>"+Convert.ToBase64String(rsaParams.DQ)+"</DQ>");
331                 sb.Append("<InverseQ>"+Convert.ToBase64String(rsaParams.InverseQ)+"</InverseQ>");
332                 sb.Append("<D>"+Convert.ToBase64String(rsaParams.D)+"</D>");
333             } 
334             sb.Append("</RSAKeyValue>");
335             return(sb.ToString());
336         }
337
338         abstract public RSAParameters ExportParameters(bool includePrivateParameters);
339
340         abstract public void ImportParameters(RSAParameters parameters);
341     }
342 }