2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System / System.Security.Cryptography.X509Certificates / X509SubjectKeyIdentifierExtension.cs
blob938a171de626aea476d48768a528fefc150da43a
1 //
2 // System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension
3 //
4 // Authors:
5 // Tim Coleman (tim@timcoleman.com)
6 // Sebastien Pouliot <sebastien@ximian.com>
7 //
8 // Copyright (C) Tim Coleman, 2004
9 // Copyright (C) 2004-2005 Novell Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #if NET_2_0 && SECURITY_DEP
33 using System.Text;
35 using Mono.Security;
36 using Mono.Security.Cryptography;
38 namespace System.Security.Cryptography.X509Certificates {
40 public sealed class X509SubjectKeyIdentifierExtension : X509Extension {
42 internal const string oid = "2.5.29.14";
43 internal const string friendlyName = "Subject Key Identifier";
45 private byte[] _subjectKeyIdentifier;
46 private string _ski;
47 private AsnDecodeStatus _status;
49 // constructors
51 public X509SubjectKeyIdentifierExtension ()
53 _oid = new Oid (oid, friendlyName);
56 public X509SubjectKeyIdentifierExtension (AsnEncodedData encodedSubjectKeyIdentifier, bool critical)
58 // ignore the Oid provided by encodedKeyUsage (our rules!)
59 _oid = new Oid (oid, friendlyName);
60 _raw = encodedSubjectKeyIdentifier.RawData;
61 base.Critical = critical;
62 _status = Decode (this.RawData);
65 public X509SubjectKeyIdentifierExtension (byte[] subjectKeyIdentifier, bool critical)
67 if (subjectKeyIdentifier == null)
68 throw new ArgumentNullException ("subjectKeyIdentifier");
69 if (subjectKeyIdentifier.Length == 0)
70 throw new ArgumentException ("subjectKeyIdentifier");
72 _oid = new Oid (oid, friendlyName);
73 base.Critical = critical;
74 _subjectKeyIdentifier = (byte[])subjectKeyIdentifier.Clone ();
75 RawData = Encode ();
78 public X509SubjectKeyIdentifierExtension (string subjectKeyIdentifier, bool critical)
80 if (subjectKeyIdentifier == null)
81 throw new ArgumentNullException ("subjectKeyIdentifier");
82 if (subjectKeyIdentifier.Length < 2)
83 throw new ArgumentException ("subjectKeyIdentifier");
85 _oid = new Oid (oid, friendlyName);
86 base.Critical = critical;
87 _subjectKeyIdentifier = FromHex (subjectKeyIdentifier);
88 RawData = Encode ();
91 public X509SubjectKeyIdentifierExtension (PublicKey key, bool critical)
92 : this (key, X509SubjectKeyIdentifierHashAlgorithm.Sha1, critical)
96 public X509SubjectKeyIdentifierExtension (PublicKey key, X509SubjectKeyIdentifierHashAlgorithm algorithm, bool critical)
98 if (key == null)
99 throw new ArgumentNullException ("key");
101 byte[] pkraw = key.EncodedKeyValue.RawData;
102 // compute SKI
103 switch (algorithm) {
104 // hash of the public key, excluding Tag, Length and unused bits values
105 case X509SubjectKeyIdentifierHashAlgorithm.Sha1:
106 _subjectKeyIdentifier = SHA1.Create ().ComputeHash (pkraw);
107 break;
108 // 0100 bit pattern followed by the 60 last bit of the hash
109 case X509SubjectKeyIdentifierHashAlgorithm.ShortSha1:
110 byte[] hash = SHA1.Create ().ComputeHash (pkraw);
111 _subjectKeyIdentifier = new byte [8];
112 Buffer.BlockCopy (hash, 12, _subjectKeyIdentifier, 0, 8);
113 _subjectKeyIdentifier [0] = (byte) (0x40 | (_subjectKeyIdentifier [0] & 0x0F));
114 break;
115 // hash of the public key, including Tag, Length and unused bits values
116 case X509SubjectKeyIdentifierHashAlgorithm.CapiSha1:
117 // CryptoAPI does that hash on the complete subjectPublicKeyInfo (unlike PKIX)
118 // http://groups.google.ca/groups?selm=e7RqM%24plCHA.1488%40tkmsftngp02&oe=UTF-8&output=gplain
119 ASN1 subjectPublicKeyInfo = new ASN1 (0x30);
120 ASN1 algo = subjectPublicKeyInfo.Add (new ASN1 (0x30));
121 algo.Add (new ASN1 (CryptoConfig.EncodeOID (key.Oid.Value)));
122 algo.Add (new ASN1 (key.EncodedParameters.RawData));
123 // add an extra byte for the unused bits (none)
124 byte[] full = new byte [pkraw.Length + 1];
125 Buffer.BlockCopy (pkraw, 0, full, 1, pkraw.Length);
126 subjectPublicKeyInfo.Add (new ASN1 (0x03, full));
127 _subjectKeyIdentifier = SHA1.Create ().ComputeHash (subjectPublicKeyInfo.GetBytes ());
128 break;
129 default:
130 throw new ArgumentException ("algorithm");
133 _oid = new Oid (oid, friendlyName);
134 base.Critical = critical;
135 RawData = Encode ();
138 // properties
140 public string SubjectKeyIdentifier {
141 get {
142 switch (_status) {
143 case AsnDecodeStatus.Ok:
144 case AsnDecodeStatus.InformationNotAvailable:
145 if (_subjectKeyIdentifier != null)
146 _ski = CryptoConvert.ToHex (_subjectKeyIdentifier);
147 return _ski;
148 default:
149 throw new CryptographicException ("Badly encoded extension.");
154 // methods
156 public override void CopyFrom (AsnEncodedData encodedData)
158 if (encodedData == null)
159 throw new ArgumentNullException ("encodedData");
161 X509Extension ex = (encodedData as X509Extension);
162 if (ex == null)
163 throw new ArgumentException (Locale.GetText ("Wrong type."), "encodedData");
165 if (ex._oid == null)
166 _oid = new Oid (oid, friendlyName);
167 else
168 _oid = new Oid (ex._oid);
170 RawData = ex.RawData;
171 base.Critical = ex.Critical;
172 // and we deal with the rest later
173 _status = Decode (this.RawData);
176 // internal
178 static internal byte FromHexChar (char c)
180 if ((c >= 'a') && (c <= 'f'))
181 return (byte) (c - 'a' + 10);
182 if ((c >= 'A') && (c <= 'F'))
183 return (byte) (c - 'A' + 10);
184 if ((c >= '0') && (c <= '9'))
185 return (byte) (c - '0');
186 return 255; // F
189 static internal byte FromHexChars (char c1, char c2)
191 byte result = FromHexChar (c1);
192 if (result < 255)
193 result = (byte) ((result << 4) | FromHexChar (c2));
194 return result;
197 static internal byte[] FromHex (string hex)
199 // here we can't use CryptoConvert.FromHex because we
200 // must convert any *illegal* (non hex) 2 characters
201 // to 'FF' and ignore last char on odd length
202 if (hex == null)
203 return null;
205 int length = hex.Length >> 1;
207 byte[] result = new byte [length]; // + (odd ? 1 : 0)];
208 int n = 0;
209 int i = 0;
210 while (n < length) {
211 result [n++] = FromHexChars (hex [i++], hex [i++]);
213 return result;
216 internal AsnDecodeStatus Decode (byte[] extension)
218 if ((extension == null) || (extension.Length == 0))
219 return AsnDecodeStatus.BadAsn;
220 _ski = String.Empty;
221 if (extension [0] != 0x04)
222 return AsnDecodeStatus.BadTag;
223 if (extension.Length == 2)
224 return AsnDecodeStatus.InformationNotAvailable;
225 if (extension.Length < 3)
226 return AsnDecodeStatus.BadLength;
228 try {
229 ASN1 ex = new ASN1 (extension);
230 _subjectKeyIdentifier = ex.Value;
232 catch {
233 return AsnDecodeStatus.BadAsn;
236 return AsnDecodeStatus.Ok;
239 internal byte[] Encode ()
241 ASN1 ex = new ASN1 (0x04, _subjectKeyIdentifier);
242 return ex.GetBytes ();
245 internal override string ToString (bool multiLine)
247 switch (_status) {
248 case AsnDecodeStatus.BadAsn:
249 return String.Empty;
250 case AsnDecodeStatus.BadTag:
251 case AsnDecodeStatus.BadLength:
252 return FormatUnkownData (_raw);
253 case AsnDecodeStatus.InformationNotAvailable:
254 return "Information Not Available";
257 if (_oid.Value != oid)
258 return String.Format ("Unknown Key Usage ({0})", _oid.Value);
260 StringBuilder sb = new StringBuilder ();
262 for (int i=0; i < _subjectKeyIdentifier.Length; i++) {
263 sb.Append (_subjectKeyIdentifier [i].ToString ("x2"));
264 if (i != _subjectKeyIdentifier.Length - 1)
265 sb.Append (" ");
268 if (multiLine)
269 sb.Append (Environment.NewLine);
271 return sb.ToString ();
276 #endif