Avoid ANE when a key algorithm parameters is really null (not just ASN.1 null)
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / PublicKey.cs
1 //
2 // PublicKey.cs - System.Security.Cryptography.PublicKey
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //      Tim Coleman (tim@timcoleman.com)
7 //
8 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) Tim Coleman, 2004
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 #if SECURITY_DEP || MOONLIGHT
33
34 using Mono.Security;
35 using Mono.Security.Cryptography;
36 using MSX = Mono.Security.X509;
37
38 namespace System.Security.Cryptography.X509Certificates {
39
40         public sealed class PublicKey {
41
42                 private const string rsaOid = "1.2.840.113549.1.1.1";
43                 private const string dsaOid = "1.2.840.10040.4.1";
44
45                 private AsymmetricAlgorithm _key;
46                 private AsnEncodedData _keyValue;
47                 private AsnEncodedData _params;
48                 private Oid _oid;
49
50                 static byte[] Empty = new byte [0];
51
52                 public PublicKey (Oid oid, AsnEncodedData parameters, AsnEncodedData keyValue)
53                 {
54                         if (oid == null)
55                                 throw new ArgumentNullException ("oid");
56                         if (parameters == null)
57                                 throw new ArgumentNullException ("parameters");
58                         if (keyValue == null)
59                                 throw new ArgumentNullException ("keyValue");
60
61                         _oid = new Oid (oid);
62                         _params = new AsnEncodedData (parameters);
63                         _keyValue = new AsnEncodedData (keyValue);
64                 }
65
66                 internal PublicKey (MSX.X509Certificate certificate)
67                 {
68                         // note: _key MUSTonly contains the public part of the key
69                         bool export_required = true;
70
71                         if (certificate.KeyAlgorithm == rsaOid) {
72 #if !MOONLIGHT
73                                 // shortcut export/import in the case the private key isn't available
74                                 RSACryptoServiceProvider rcsp = (certificate.RSA as RSACryptoServiceProvider);
75                                 if ((rcsp != null) && rcsp.PublicOnly) {
76                                         _key = certificate.RSA;
77                                         export_required = false;
78                                 } else 
79 #endif
80                                 {
81                                         RSAManaged rsam = (certificate.RSA as RSAManaged);
82                                         if ((rsam != null) && rsam.PublicOnly) {
83                                                 _key = certificate.RSA;
84                                                 export_required = false;
85                                         }
86                                 }
87
88                                 if (export_required) {
89                                         RSAParameters rsap = certificate.RSA.ExportParameters (false);
90                                         _key = RSA.Create ();
91                                         (_key as RSA).ImportParameters (rsap);
92                                 }
93                         } else {
94 #if !MOONLIGHT
95                                 // shortcut export/import in the case the private key isn't available
96                                 DSACryptoServiceProvider dcsp = (certificate.DSA as DSACryptoServiceProvider);
97                                 if ((dcsp != null) && dcsp.PublicOnly) {
98                                         _key = certificate.DSA;
99                                         export_required = false;
100                                 }
101                                 // note: DSAManaged isn't available in Mono.Security due to a bug in Fx 1.x
102
103                                 if (export_required) {
104                                         DSAParameters rsap = certificate.DSA.ExportParameters (false);
105                                         _key = DSA.Create ();
106                                         (_key as DSA).ImportParameters (rsap);
107                                 }
108 #endif
109                         }
110
111                         _oid = new Oid (certificate.KeyAlgorithm);
112                         _keyValue = new AsnEncodedData (_oid, certificate.PublicKey);
113                         _params = new AsnEncodedData (_oid, certificate.KeyAlgorithmParameters ?? Empty);
114                 }
115
116                 // properties
117
118                 public AsnEncodedData EncodedKeyValue {
119                         get { return _keyValue; }
120                 }
121
122                 public AsnEncodedData EncodedParameters {
123                         get { return _params; }
124                 }
125
126                 public AsymmetricAlgorithm Key {
127                         get {
128                                 if (_key == null) {
129                                         switch (_oid.Value) {
130                                         case rsaOid:
131                                                 _key = DecodeRSA (_keyValue.RawData);
132                                                 break;
133                                         case dsaOid:
134                                                 _key = DecodeDSA (_keyValue.RawData, _params.RawData);
135                                                 break;
136                                         default:
137                                                 string msg = Locale.GetText ("Cannot decode public key from unknown OID '{0}'.", _oid.Value);
138                                                 throw new NotSupportedException (msg);
139                                         }
140                                 }
141                                 return _key;
142                         }
143                 }
144
145                 public Oid Oid {
146                         get { return _oid; }
147                 }
148
149                 // private stuff
150
151                 static private byte[] GetUnsignedBigInteger (byte[] integer) 
152                 {
153                         if (integer [0] != 0x00)
154                                 return integer;
155
156                         // this first byte is added so we're sure it's an unsigned integer
157                         // however we can't feed it into RSAParameters or DSAParameters
158                         int length = integer.Length - 1;
159                         byte[] uinteger = new byte [length];
160                         Buffer.BlockCopy (integer, 1, uinteger, 0, length);
161                         return uinteger;
162                 }
163
164                 static internal DSA DecodeDSA (byte[] rawPublicKey, byte[] rawParameters)
165                 {
166                         DSAParameters dsaParams = new DSAParameters ();
167                         try {
168                                 // for DSA rawPublicKey contains 1 ASN.1 integer - Y
169                                 ASN1 pubkey = new ASN1 (rawPublicKey);
170                                 if (pubkey.Tag != 0x02)
171                                         throw new CryptographicException (Locale.GetText ("Missing DSA Y integer."));
172                                 dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
173
174                                 ASN1 param = new ASN1 (rawParameters);
175                                 if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
176                                         throw new CryptographicException (Locale.GetText ("Missing DSA parameters."));
177                                 if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
178                                         throw new CryptographicException (Locale.GetText ("Invalid DSA parameters."));
179
180                                 dsaParams.P = GetUnsignedBigInteger (param [0].Value);
181                                 dsaParams.Q = GetUnsignedBigInteger (param [1].Value);
182                                 dsaParams.G = GetUnsignedBigInteger (param [2].Value);
183                         }
184                         catch (Exception e) {
185                                 string msg = Locale.GetText ("Error decoding the ASN.1 structure.");
186                                 throw new CryptographicException (msg, e);
187                         }
188
189 #if MOONLIGHT
190                         DSA dsa = (DSA) new DSAManaged (dsaParams.Y.Length << 3);
191 #else
192                         DSA dsa = (DSA) new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
193 #endif
194                         dsa.ImportParameters (dsaParams);
195                         return dsa;
196                 }
197
198                 static internal RSA DecodeRSA (byte[] rawPublicKey)
199                 {
200                         RSAParameters rsaParams = new RSAParameters ();
201                         try {
202                                 // for RSA rawPublicKey contains 2 ASN.1 integers
203                                 // the modulus and the public exponent
204                                 ASN1 pubkey = new ASN1 (rawPublicKey);
205                                 if (pubkey.Count == 0)
206                                         throw new CryptographicException (Locale.GetText ("Missing RSA modulus and exponent."));
207                                 ASN1 modulus = pubkey [0];
208                                 if ((modulus == null) || (modulus.Tag != 0x02))
209                                         throw new CryptographicException (Locale.GetText ("Missing RSA modulus."));
210                                 ASN1 exponent = pubkey [1];
211                                 if (exponent.Tag != 0x02)
212                                         throw new CryptographicException (Locale.GetText ("Missing RSA public exponent."));
213
214                                 rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value);
215                                 rsaParams.Exponent = exponent.Value;
216                         }
217                         catch (Exception e) {
218                                 string msg = Locale.GetText ("Error decoding the ASN.1 structure.");
219                                 throw new CryptographicException (msg, e);
220                         }
221
222                         int keySize = (rsaParams.Modulus.Length << 3);
223 #if MOONLIGHT
224                         RSA rsa = (RSA) new RSAManaged (keySize);
225 #else
226                         RSA rsa = (RSA) new RSACryptoServiceProvider (keySize);
227 #endif
228                         rsa.ImportParameters (rsaParams);
229                         return rsa;
230                 }
231         }
232 }
233
234 #endif