2 // CryptoConvert.cs - Crypto Convertion Routines
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Globalization
;
32 using System
.Security
.Cryptography
;
35 namespace Mono
.Security
.Cryptography
{
42 sealed class CryptoConvert
{
44 private CryptoConvert ()
48 static private int ToInt32LE (byte [] bytes
, int offset
)
50 return (bytes
[offset
+3] << 24) | (bytes
[offset
+2] << 16) | (bytes
[offset
+1] << 8) | bytes
[offset
];
53 static private uint ToUInt32LE (byte [] bytes
, int offset
)
55 return (uint)((bytes
[offset
+3] << 24) | (bytes
[offset
+2] << 16) | (bytes
[offset
+1] << 8) | bytes
[offset
]);
58 static private byte [] GetBytesLE (int val
)
62 (byte) ((val
>> 8) & 0xff),
63 (byte) ((val
>> 16) & 0xff),
64 (byte) ((val
>> 24) & 0xff)
68 static private byte[] Trim (byte[] array
)
70 for (int i
=0; i
< array
.Length
; i
++) {
71 if (array
[i
] != 0x00) {
72 byte[] result
= new byte [array
.Length
- i
];
73 Buffer
.BlockCopy (array
, i
, result
, 0, result
.Length
);
80 // convert the key from PRIVATEKEYBLOB to RSA
81 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
82 // e.g. SNK files, PVK files
83 static public RSA
FromCapiPrivateKeyBlob (byte[] blob
)
85 return FromCapiPrivateKeyBlob (blob
, 0);
88 static public RSA
FromCapiPrivateKeyBlob (byte[] blob
, int offset
)
91 throw new ArgumentNullException ("blob");
92 if (offset
>= blob
.Length
)
93 throw new ArgumentException ("blob is too small.");
95 RSAParameters rsap
= new RSAParameters ();
97 if ((blob
[offset
] != 0x07) || // PRIVATEKEYBLOB (0x07)
98 (blob
[offset
+1] != 0x02) || // Version (0x02)
99 (blob
[offset
+2] != 0x00) || // Reserved (word)
100 (blob
[offset
+3] != 0x00) ||
101 (ToUInt32LE (blob
, offset
+8) != 0x32415352)) // DWORD magic = RSA2
102 throw new CryptographicException ("Invalid blob header");
104 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
105 // int algId = ToInt32LE (blob, offset+4);
108 int bitLen
= ToInt32LE (blob
, offset
+12);
110 // DWORD public exponent
111 byte[] exp
= new byte [4];
112 Buffer
.BlockCopy (blob
, offset
+16, exp
, 0, 4);
114 rsap
.Exponent
= Trim (exp
);
117 // BYTE modulus[rsapubkey.bitlen/8];
118 int byteLen
= (bitLen
>> 3);
119 rsap
.Modulus
= new byte [byteLen
];
120 Buffer
.BlockCopy (blob
, pos
, rsap
.Modulus
, 0, byteLen
);
121 Array
.Reverse (rsap
.Modulus
);
124 // BYTE prime1[rsapubkey.bitlen/16];
125 int byteHalfLen
= (byteLen
>> 1);
126 rsap
.P
= new byte [byteHalfLen
];
127 Buffer
.BlockCopy (blob
, pos
, rsap
.P
, 0, byteHalfLen
);
128 Array
.Reverse (rsap
.P
);
131 // BYTE prime2[rsapubkey.bitlen/16];
132 rsap
.Q
= new byte [byteHalfLen
];
133 Buffer
.BlockCopy (blob
, pos
, rsap
.Q
, 0, byteHalfLen
);
134 Array
.Reverse (rsap
.Q
);
137 // BYTE exponent1[rsapubkey.bitlen/16];
138 rsap
.DP
= new byte [byteHalfLen
];
139 Buffer
.BlockCopy (blob
, pos
, rsap
.DP
, 0, byteHalfLen
);
140 Array
.Reverse (rsap
.DP
);
143 // BYTE exponent2[rsapubkey.bitlen/16];
144 rsap
.DQ
= new byte [byteHalfLen
];
145 Buffer
.BlockCopy (blob
, pos
, rsap
.DQ
, 0, byteHalfLen
);
146 Array
.Reverse (rsap
.DQ
);
149 // BYTE coefficient[rsapubkey.bitlen/16];
150 rsap
.InverseQ
= new byte [byteHalfLen
];
151 Buffer
.BlockCopy (blob
, pos
, rsap
.InverseQ
, 0, byteHalfLen
);
152 Array
.Reverse (rsap
.InverseQ
);
155 // ok, this is hackish but CryptoAPI support it so...
156 // note: only works because CRT is used by default
157 // http://bugzilla.ximian.com/show_bug.cgi?id=57941
158 rsap
.D
= new byte [byteLen
]; // must be allocated
159 if (pos
+ byteLen
+ offset
<= blob
.Length
) {
160 // BYTE privateExponent[rsapubkey.bitlen/8];
161 Buffer
.BlockCopy (blob
, pos
, rsap
.D
, 0, byteLen
);
162 Array
.Reverse (rsap
.D
);
165 catch (Exception e
) {
166 throw new CryptographicException ("Invalid blob.", e
);
170 RSA rsa
= RSA
.Create ();
171 rsa
.ImportParameters (rsap
);
176 rsa
.ImportParameters (rsap
);
178 catch (CryptographicException ce
) {
179 // this may cause problem when this code is run under
180 // the SYSTEM identity on Windows (e.g. ASP.NET). See
181 // http://bugzilla.ximian.com/show_bug.cgi?id=77559
183 CspParameters csp
= new CspParameters ();
184 csp
.Flags
= CspProviderFlags
.UseMachineKeyStore
;
185 rsa
= new RSACryptoServiceProvider (csp
);
186 rsa
.ImportParameters (rsap
);
189 // rethrow original, not the later, exception if this fails
197 static public DSA
FromCapiPrivateKeyBlobDSA (byte[] blob
)
199 return FromCapiPrivateKeyBlobDSA (blob
, 0);
202 static public DSA
FromCapiPrivateKeyBlobDSA (byte[] blob
, int offset
)
205 throw new ArgumentNullException ("blob");
206 if (offset
>= blob
.Length
)
207 throw new ArgumentException ("blob is too small.");
209 DSAParameters dsap
= new DSAParameters ();
211 if ((blob
[offset
] != 0x07) || // PRIVATEKEYBLOB (0x07)
212 (blob
[offset
+ 1] != 0x02) || // Version (0x02)
213 (blob
[offset
+ 2] != 0x00) || // Reserved (word)
214 (blob
[offset
+ 3] != 0x00) ||
215 (ToUInt32LE (blob
, offset
+ 8) != 0x32535344)) // DWORD magic
216 throw new CryptographicException ("Invalid blob header");
218 int bitlen
= ToInt32LE (blob
, offset
+ 12);
219 int bytelen
= bitlen
>> 3;
220 int pos
= offset
+ 16;
222 dsap
.P
= new byte [bytelen
];
223 Buffer
.BlockCopy (blob
, pos
, dsap
.P
, 0, bytelen
);
224 Array
.Reverse (dsap
.P
);
227 dsap
.Q
= new byte [20];
228 Buffer
.BlockCopy (blob
, pos
, dsap
.Q
, 0, 20);
229 Array
.Reverse (dsap
.Q
);
232 dsap
.G
= new byte [bytelen
];
233 Buffer
.BlockCopy (blob
, pos
, dsap
.G
, 0, bytelen
);
234 Array
.Reverse (dsap
.G
);
237 dsap
.X
= new byte [20];
238 Buffer
.BlockCopy (blob
, pos
, dsap
.X
, 0, 20);
239 Array
.Reverse (dsap
.X
);
242 dsap
.Counter
= ToInt32LE (blob
, pos
);
245 dsap
.Seed
= new byte [20];
246 Buffer
.BlockCopy (blob
, pos
, dsap
.Seed
, 0, 20);
247 Array
.Reverse (dsap
.Seed
);
250 catch (Exception e
) {
251 throw new CryptographicException ("Invalid blob.", e
);
255 DSA dsa
= (DSA
)DSA
.Create ();
256 dsa
.ImportParameters (dsap
);
260 dsa
= (DSA
)DSA
.Create ();
261 dsa
.ImportParameters (dsap
);
263 catch (CryptographicException ce
) {
264 // this may cause problem when this code is run under
265 // the SYSTEM identity on Windows (e.g. ASP.NET). See
266 // http://bugzilla.ximian.com/show_bug.cgi?id=77559
268 CspParameters csp
= new CspParameters ();
269 csp
.Flags
= CspProviderFlags
.UseMachineKeyStore
;
270 dsa
= new DSACryptoServiceProvider (csp
);
271 dsa
.ImportParameters (dsap
);
274 // rethrow original, not the later, exception if this fails
282 static public byte[] ToCapiPrivateKeyBlob (RSA rsa
)
284 RSAParameters p
= rsa
.ExportParameters (true);
285 int keyLength
= p
.Modulus
.Length
; // in bytes
286 byte[] blob
= new byte [20 + (keyLength
<< 2) + (keyLength
>> 1)];
288 blob
[0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
289 blob
[1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
290 // [2], [3] // RESERVED - Always 0
291 blob
[5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
292 blob
[8] = 0x52; // Magic - RSA2 (ASCII in hex)
297 byte[] bitlen
= GetBytesLE (keyLength
<< 3);
298 blob
[12] = bitlen
[0]; // bitlen
299 blob
[13] = bitlen
[1];
300 blob
[14] = bitlen
[2];
301 blob
[15] = bitlen
[3];
303 // public exponent (DWORD)
305 int n
= p
.Exponent
.Length
;
307 blob
[pos
++] = p
.Exponent
[--n
];
310 byte[] part
= p
.Modulus
;
311 int len
= part
.Length
;
312 Array
.Reverse (part
, 0, len
);
313 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
318 Array
.Reverse (part
, 0, len
);
319 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
324 Array
.Reverse (part
, 0, len
);
325 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
330 Array
.Reverse (part
, 0, len
);
331 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
336 Array
.Reverse (part
, 0, len
);
337 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
342 Array
.Reverse (part
, 0, len
);
343 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
348 Array
.Reverse (part
, 0, len
);
349 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
354 static public byte[] ToCapiPrivateKeyBlob (DSA dsa
)
356 DSAParameters p
= dsa
.ExportParameters (true);
357 int keyLength
= p
.P
.Length
; // in bytes
359 // header + P + Q + G + X + count + seed
360 byte[] blob
= new byte [16 + keyLength
+ 20 + keyLength
+ 20 + 4 + 20];
362 blob
[0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
363 blob
[1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
364 // [2], [3] // RESERVED - Always 0
365 blob
[5] = 0x22; // ALGID
366 blob
[8] = 0x44; // Magic
371 byte[] bitlen
= GetBytesLE (keyLength
<< 3);
372 blob
[12] = bitlen
[0];
373 blob
[13] = bitlen
[1];
374 blob
[14] = bitlen
[2];
375 blob
[15] = bitlen
[3];
379 Array
.Reverse (part
);
380 Buffer
.BlockCopy (part
, 0, blob
, pos
, keyLength
);
384 Array
.Reverse (part
);
385 Buffer
.BlockCopy (part
, 0, blob
, pos
, 20);
389 Array
.Reverse (part
);
390 Buffer
.BlockCopy (part
, 0, blob
, pos
, keyLength
);
394 Array
.Reverse (part
);
395 Buffer
.BlockCopy (part
, 0, blob
, pos
, 20);
398 Buffer
.BlockCopy (GetBytesLE (p
.Counter
), 0, blob
, pos
, 4);
402 Array
.Reverse (part
);
403 Buffer
.BlockCopy (part
, 0, blob
, pos
, 20);
408 static public RSA
FromCapiPublicKeyBlob (byte[] blob
)
410 return FromCapiPublicKeyBlob (blob
, 0);
413 static public RSA
FromCapiPublicKeyBlob (byte[] blob
, int offset
)
416 throw new ArgumentNullException ("blob");
417 if (offset
>= blob
.Length
)
418 throw new ArgumentException ("blob is too small.");
421 if ((blob
[offset
] != 0x06) || // PUBLICKEYBLOB (0x06)
422 (blob
[offset
+1] != 0x02) || // Version (0x02)
423 (blob
[offset
+2] != 0x00) || // Reserved (word)
424 (blob
[offset
+3] != 0x00) ||
425 (ToUInt32LE (blob
, offset
+8) != 0x31415352)) // DWORD magic = RSA1
426 throw new CryptographicException ("Invalid blob header");
428 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
429 // int algId = ToInt32LE (blob, offset+4);
432 int bitLen
= ToInt32LE (blob
, offset
+12);
434 // DWORD public exponent
435 RSAParameters rsap
= new RSAParameters ();
436 rsap
.Exponent
= new byte [3];
437 rsap
.Exponent
[0] = blob
[offset
+18];
438 rsap
.Exponent
[1] = blob
[offset
+17];
439 rsap
.Exponent
[2] = blob
[offset
+16];
442 // BYTE modulus[rsapubkey.bitlen/8];
443 int byteLen
= (bitLen
>> 3);
444 rsap
.Modulus
= new byte [byteLen
];
445 Buffer
.BlockCopy (blob
, pos
, rsap
.Modulus
, 0, byteLen
);
446 Array
.Reverse (rsap
.Modulus
);
448 RSA rsa
= RSA
.Create ();
449 rsa
.ImportParameters (rsap
);
454 rsa
.ImportParameters (rsap
);
456 catch (CryptographicException
) {
457 // this may cause problem when this code is run under
458 // the SYSTEM identity on Windows (e.g. ASP.NET). See
459 // http://bugzilla.ximian.com/show_bug.cgi?id=77559
460 CspParameters csp
= new CspParameters ();
461 csp
.Flags
= CspProviderFlags
.UseMachineKeyStore
;
462 rsa
= new RSACryptoServiceProvider (csp
);
463 rsa
.ImportParameters (rsap
);
468 catch (Exception e
) {
469 throw new CryptographicException ("Invalid blob.", e
);
473 static public DSA
FromCapiPublicKeyBlobDSA (byte[] blob
)
475 return FromCapiPublicKeyBlobDSA (blob
, 0);
478 static public DSA
FromCapiPublicKeyBlobDSA (byte[] blob
, int offset
)
481 throw new ArgumentNullException ("blob");
482 if (offset
>= blob
.Length
)
483 throw new ArgumentException ("blob is too small.");
486 if ((blob
[offset
] != 0x06) || // PUBLICKEYBLOB (0x06)
487 (blob
[offset
+ 1] != 0x02) || // Version (0x02)
488 (blob
[offset
+ 2] != 0x00) || // Reserved (word)
489 (blob
[offset
+ 3] != 0x00) ||
490 (ToUInt32LE (blob
, offset
+ 8) != 0x31535344)) // DWORD magic
491 throw new CryptographicException ("Invalid blob header");
493 int bitlen
= ToInt32LE (blob
, offset
+ 12);
494 DSAParameters dsap
= new DSAParameters ();
495 int bytelen
= bitlen
>> 3;
496 int pos
= offset
+ 16;
498 dsap
.P
= new byte [bytelen
];
499 Buffer
.BlockCopy (blob
, pos
, dsap
.P
, 0, bytelen
);
500 Array
.Reverse (dsap
.P
);
503 dsap
.Q
= new byte [20];
504 Buffer
.BlockCopy (blob
, pos
, dsap
.Q
, 0, 20);
505 Array
.Reverse (dsap
.Q
);
508 dsap
.G
= new byte [bytelen
];
509 Buffer
.BlockCopy (blob
, pos
, dsap
.G
, 0, bytelen
);
510 Array
.Reverse (dsap
.G
);
513 dsap
.Y
= new byte [bytelen
];
514 Buffer
.BlockCopy (blob
, pos
, dsap
.Y
, 0, bytelen
);
515 Array
.Reverse (dsap
.Y
);
518 dsap
.Counter
= ToInt32LE (blob
, pos
);
521 dsap
.Seed
= new byte [20];
522 Buffer
.BlockCopy (blob
, pos
, dsap
.Seed
, 0, 20);
523 Array
.Reverse (dsap
.Seed
);
526 DSA dsa
= (DSA
)DSA
.Create ();
527 dsa
.ImportParameters (dsap
);
530 catch (Exception e
) {
531 throw new CryptographicException ("Invalid blob.", e
);
535 static public byte[] ToCapiPublicKeyBlob (RSA rsa
)
537 RSAParameters p
= rsa
.ExportParameters (false);
538 int keyLength
= p
.Modulus
.Length
; // in bytes
539 byte[] blob
= new byte [20 + keyLength
];
541 blob
[0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
542 blob
[1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
543 // [2], [3] // RESERVED - Always 0
544 blob
[5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
545 blob
[8] = 0x52; // Magic - RSA1 (ASCII in hex)
550 byte[] bitlen
= GetBytesLE (keyLength
<< 3);
551 blob
[12] = bitlen
[0]; // bitlen
552 blob
[13] = bitlen
[1];
553 blob
[14] = bitlen
[2];
554 blob
[15] = bitlen
[3];
556 // public exponent (DWORD)
558 int n
= p
.Exponent
.Length
;
560 blob
[pos
++] = p
.Exponent
[--n
];
563 byte[] part
= p
.Modulus
;
564 int len
= part
.Length
;
565 Array
.Reverse (part
, 0, len
);
566 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
571 static public byte[] ToCapiPublicKeyBlob (DSA dsa
)
573 DSAParameters p
= dsa
.ExportParameters (false);
574 int keyLength
= p
.P
.Length
; // in bytes
576 // header + P + Q + G + Y + count + seed
577 byte[] blob
= new byte [16 + keyLength
+ 20 + keyLength
+ keyLength
+ 4 + 20];
579 blob
[0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
580 blob
[1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
581 // [2], [3] // RESERVED - Always 0
582 blob
[5] = 0x22; // ALGID
583 blob
[8] = 0x44; // Magic
588 byte[] bitlen
= GetBytesLE (keyLength
<< 3);
589 blob
[12] = bitlen
[0];
590 blob
[13] = bitlen
[1];
591 blob
[14] = bitlen
[2];
592 blob
[15] = bitlen
[3];
598 Array
.Reverse (part
);
599 Buffer
.BlockCopy (part
, 0, blob
, pos
, keyLength
);
603 Array
.Reverse (part
);
604 Buffer
.BlockCopy (part
, 0, blob
, pos
, 20);
608 Array
.Reverse (part
);
609 Buffer
.BlockCopy (part
, 0, blob
, pos
, keyLength
);
613 Array
.Reverse (part
);
614 Buffer
.BlockCopy (part
, 0, blob
, pos
, keyLength
);
617 Buffer
.BlockCopy (GetBytesLE (p
.Counter
), 0, blob
, pos
, 4);
621 Array
.Reverse (part
);
622 Buffer
.BlockCopy (part
, 0, blob
, pos
, 20);
629 static public RSA
FromCapiKeyBlob (byte[] blob
)
631 return FromCapiKeyBlob (blob
, 0);
634 static public RSA
FromCapiKeyBlob (byte[] blob
, int offset
)
637 throw new ArgumentNullException ("blob");
638 if (offset
>= blob
.Length
)
639 throw new ArgumentException ("blob is too small.");
641 switch (blob
[offset
]) {
643 // this could be a public key inside an header
644 // like "sn -e" would produce
645 if (blob
[offset
+ 12] == 0x06) {
646 return FromCapiPublicKeyBlob (blob
, offset
+ 12);
650 return FromCapiPublicKeyBlob (blob
, offset
);
652 return FromCapiPrivateKeyBlob (blob
, offset
);
654 throw new CryptographicException ("Unknown blob format.");
657 static public DSA
FromCapiKeyBlobDSA (byte[] blob
)
659 return FromCapiKeyBlobDSA (blob
, 0);
662 static public DSA
FromCapiKeyBlobDSA (byte[] blob
, int offset
)
665 throw new ArgumentNullException ("blob");
666 if (offset
>= blob
.Length
)
667 throw new ArgumentException ("blob is too small.");
669 switch (blob
[offset
]) {
671 return FromCapiPublicKeyBlobDSA (blob
, offset
);
673 return FromCapiPrivateKeyBlobDSA (blob
, offset
);
675 throw new CryptographicException ("Unknown blob format.");
678 static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair
, bool includePrivateKey
)
681 throw new ArgumentNullException ("keypair");
683 // check between RSA and DSA (and potentially others like DH)
685 return ToCapiKeyBlob ((RSA
)keypair
, includePrivateKey
);
686 else if (keypair
is DSA
)
687 return ToCapiKeyBlob ((DSA
)keypair
, includePrivateKey
);
692 static public byte[] ToCapiKeyBlob (RSA rsa
, bool includePrivateKey
)
695 throw new ArgumentNullException ("rsa");
697 if (includePrivateKey
)
698 return ToCapiPrivateKeyBlob (rsa
);
700 return ToCapiPublicKeyBlob (rsa
);
703 static public byte[] ToCapiKeyBlob (DSA dsa
, bool includePrivateKey
)
706 throw new ArgumentNullException ("dsa");
708 if (includePrivateKey
)
709 return ToCapiPrivateKeyBlob (dsa
);
711 return ToCapiPublicKeyBlob (dsa
);
714 static public string ToHex (byte[] input
)
719 StringBuilder sb
= new StringBuilder (input
.Length
* 2);
720 foreach (byte b
in input
) {
721 sb
.Append (b
.ToString ("X2", CultureInfo
.InvariantCulture
));
723 return sb
.ToString ();
726 static private byte FromHexChar (char c
)
728 if ((c
>= 'a') && (c
<= 'f'))
729 return (byte) (c
- 'a' + 10);
730 if ((c
>= 'A') && (c
<= 'F'))
731 return (byte) (c
- 'A' + 10);
732 if ((c
>= '0') && (c
<= '9'))
733 return (byte) (c
- '0');
734 throw new ArgumentException ("invalid hex char");
737 static public byte[] FromHex (string hex
)
741 if ((hex
.Length
& 0x1) == 0x1)
742 throw new ArgumentException ("Length must be a multiple of 2");
744 byte[] result
= new byte [hex
.Length
>> 1];
747 while (n
< result
.Length
) {
748 result
[n
] = (byte) (FromHexChar (hex
[i
++]) << 4);
749 result
[n
++] += FromHexChar (hex
[i
++]);