3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>Microsoft</OWNER>
13 namespace System.Security.Cryptography {
16 using System.Runtime.Serialization;
17 using System.Security.Util;
18 using System.Globalization;
19 using System.Diagnostics.Contracts;
21 // We allow only the public components of an RSAParameters object, the Modulus and Exponent
22 // to be serializable.
24 [System.Runtime.InteropServices.ComVisible(true)]
25 public struct RSAParameters {
26 public byte[] Exponent;
27 public byte[] Modulus;
33 public byte[] InverseQ;
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;
45 [System.Runtime.InteropServices.ComVisible(true)]
46 public abstract class RSA : AsymmetricAlgorithm
53 new static public RSA Create() {
55 return new System.Security.Cryptography.RSACryptoServiceProvider ();
57 return Create("System.Security.Cryptography.RSA");
61 new static public RSA Create(String algName) {
62 return (RSA) CryptoConfig.CreateFromName(algName);
66 // New RSA encrypt/decrypt/sign/verify RSA abstractions in .NET 4.6+ and .NET Core
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.
72 public virtual byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
73 throw DerivedClassMustOverride();
76 public virtual byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
77 throw DerivedClassMustOverride();
80 public virtual byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
81 throw DerivedClassMustOverride();
84 public virtual bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
85 throw DerivedClassMustOverride();
88 protected virtual byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
89 throw DerivedClassMustOverride();
92 protected virtual byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
93 throw DerivedClassMustOverride();
96 public byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
98 throw new ArgumentNullException("data");
100 return SignData(data, 0, data.Length, hashAlgorithm, padding);
103 public virtual byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
105 throw new ArgumentNullException("data");
107 if (offset < 0 || offset > data.Length) {
108 throw new ArgumentOutOfRangeException("offset");
110 if (count < 0 || count > data.Length - offset) {
111 throw new ArgumentOutOfRangeException("count");
113 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
114 throw HashAlgorithmNameNullOrEmpty();
116 if (padding == null) {
117 throw new ArgumentNullException("padding");
120 byte[] hash = HashData(data, offset, count, hashAlgorithm);
121 return SignHash(hash, hashAlgorithm, padding);
124 public virtual byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
126 throw new ArgumentNullException("data");
128 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
129 throw HashAlgorithmNameNullOrEmpty();
131 if (padding == null) {
132 throw new ArgumentNullException("padding");
135 byte[] hash = HashData(data, hashAlgorithm);
136 return SignHash(hash, hashAlgorithm, padding);
139 public bool VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
141 throw new ArgumentNullException("data");
143 return VerifyData(data, 0, data.Length, signature, hashAlgorithm, padding);
146 public virtual bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
148 throw new ArgumentNullException("data");
150 if (offset < 0 || offset > data.Length) {
151 throw new ArgumentOutOfRangeException("offset");
153 if (count < 0 || count > data.Length - offset) {
154 throw new ArgumentOutOfRangeException("count");
156 if (signature == null) {
157 throw new ArgumentNullException("signature");
159 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
160 throw HashAlgorithmNameNullOrEmpty();
162 if (padding == null) {
163 throw new ArgumentNullException("padding");
166 byte[] hash = HashData(data, offset, count, hashAlgorithm);
167 return VerifyHash(hash, signature, hashAlgorithm, padding);
170 public bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
172 throw new ArgumentNullException("data");
174 if (signature == null) {
175 throw new ArgumentNullException("signature");
177 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
178 throw HashAlgorithmNameNullOrEmpty();
180 if (padding == null) {
181 throw new ArgumentNullException("padding");
184 byte[] hash = HashData(data, hashAlgorithm);
185 return VerifyHash(hash, signature, hashAlgorithm, padding);
188 private static Exception DerivedClassMustOverride() {
189 return new NotImplementedException(Environment.GetResourceString("NotSupported_SubclassOverride"));
192 internal static Exception HashAlgorithmNameNullOrEmpty() {
193 return new ArgumentException(Environment.GetResourceString("Cryptography_HashAlgorithmNameNullOrEmpty"), "hashAlgorithm");
197 // Legacy encrypt/decrypt RSA abstraction from .NET < 4.6
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.
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.
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.
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
217 public virtual byte[] DecryptValue(byte[] rgb) {
218 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
222 public virtual byte[] EncryptValue(byte[] rgb) {
223 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
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.
230 // For new derived RSA classes, we'll just return "RSA" which is analagous to what ECDsa
231 // and ECDiffieHellman do.
233 // Note that for compat, RSACryptoServiceProvider still overrides and returns RSA-PKCS1-KEYEX
234 // and http://www.w3.org/2000/09/xmldsig#rsa-sha1
237 public override string KeyExchangeAlgorithm {
238 get { return "RSA"; }
241 public override string SignatureAlgorithm {
242 get { return "RSA"; }
246 // Import/export functions
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.
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();
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"));
264 rsaParams.Modulus = Convert.FromBase64String(Utils.DiscardWhiteSpaces(modulusString));
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"));
271 rsaParams.Exponent = Convert.FromBase64String(Utils.DiscardWhiteSpaces(exponentString));
274 String pString = topElement.SearchForTextOfLocalName("P");
275 if (pString != null) rsaParams.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(pString));
278 String qString = topElement.SearchForTextOfLocalName("Q");
279 if (qString != null) rsaParams.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(qString));
282 String dpString = topElement.SearchForTextOfLocalName("DP");
283 if (dpString != null) rsaParams.DP = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dpString));
286 String dqString = topElement.SearchForTextOfLocalName("DQ");
287 if (dqString != null) rsaParams.DQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dqString));
289 // InverseQ is optional
290 String inverseQString = topElement.SearchForTextOfLocalName("InverseQ");
291 if (inverseQString != null) rsaParams.InverseQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inverseQString));
294 String dString = topElement.SearchForTextOfLocalName("D");
295 if (dString != null) rsaParams.D = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dString));
297 ImportParameters(rsaParams);
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:
308 <element name="RSAKeyValue">
311 <element name="Modulus" type="ds:CryptoBinary"/>
312 <element name="Exponent" type="ds:CryptoBinary"/>
317 // we extend appropriately for private components
318 RSAParameters rsaParams = this.ExportParameters(includePrivateParameters);
319 StringBuilder sb = new StringBuilder();
320 sb.Append("<RSAKeyValue>");
322 sb.Append("<Modulus>"+Convert.ToBase64String(rsaParams.Modulus)+"</Modulus>");
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>");
334 sb.Append("</RSAKeyValue>");
335 return(sb.ToString());
338 abstract public RSAParameters ExportParameters(bool includePrivateParameters);
340 abstract public void ImportParameters(RSAParameters parameters);