SymmetricKeyWrap.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. //
  2. // SymmetricKeyWrap.cs - Implements symmetric key wrap algorithms
  3. //
  4. // Author:
  5. // Tim Coleman ([email protected])
  6. //
  7. // Copyright (C) Tim Coleman, 2004
  8. //
  9. //
  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:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  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.
  28. //
  29. #if NET_2_0
  30. using System.IO;
  31. using System.Security.Cryptography;
  32. namespace System.Security.Cryptography.Xml {
  33. internal class SymmetricKeyWrap {
  34. public SymmetricKeyWrap ()
  35. {
  36. }
  37. public static byte[] AESKeyWrapEncrypt (byte[] rgbKey, byte[] rgbWrappedKeyData)
  38. {
  39. SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael");
  40. // Apparently no one felt the need to document that this requires Electronic Codebook mode.
  41. symAlg.Mode = CipherMode.ECB;
  42. // This was also not documented anywhere.
  43. symAlg.IV = new byte [16] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  44. ICryptoTransform transform = symAlg.CreateEncryptor (rgbKey, symAlg.IV);
  45. int N = rgbWrappedKeyData.Length / 8;
  46. byte[] A;
  47. byte[] B = new Byte [16];
  48. byte [] C = new byte [8 * (N + 1)];
  49. // 1. if N is 1:
  50. // B = AES(K)enc(0xA6A6A6A6A6A6A6A6|P(1))
  51. // C(0) = MSB(B)
  52. // C(1) = LSB(B)
  53. if (N == 1) {
  54. A = new byte [8] {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6};
  55. transform.TransformBlock (Concatenate (A, rgbWrappedKeyData), 0, 16, B, 0);
  56. Buffer.BlockCopy (MSB(B), 0, C, 0, 8);
  57. Buffer.BlockCopy (LSB(B), 0, C, 8, 8);
  58. } else {
  59. // if N > 1, perform the following steps:
  60. // 2. Initialize variables:
  61. // Set A to 0xA6A6A6A6A6A6A6A6
  62. // For i = 1 to N,
  63. // R(i) = P(i)
  64. A = new byte [8] {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6};
  65. byte[][] R = new byte [N + 1][];
  66. for (int i = 1; i <= N; i += 1) {
  67. R [i] = new byte [8];
  68. Buffer.BlockCopy (rgbWrappedKeyData, 8 * (i - 1), R [i], 0, 8);
  69. }
  70. // 3. Calculate intermediate values:
  71. // For j = 0 to 5
  72. // For i = 1 to N
  73. // t = i + j * N
  74. // B = AES(K)enc(A|R(i))
  75. // A = XOR(t, MSB(B))
  76. // R(i) = LSB(B)
  77. for (int j = 0; j <= 5; j += 1) {
  78. for (int i = 1; i <= N; i += 1) {
  79. transform.TransformBlock (Concatenate (A, R [i]), 0, 16, B, 0);
  80. // Yawn. It was nice of those at NIST to document how exactly we should XOR
  81. // an integer value with a byte array. Not.
  82. byte[] T = BitConverter.GetBytes ((long) (N * j + i));
  83. // This is nice.
  84. if (BitConverter.IsLittleEndian)
  85. Array.Reverse (T);
  86. A = Xor (T, MSB(B));
  87. R [i] = LSB (B);
  88. }
  89. }
  90. // 4. Output the results:
  91. // Set C(0) = A
  92. // For i = 1 to N
  93. // C(i) = R(i)
  94. Buffer.BlockCopy (A, 0, C, 0, 8);
  95. for (int i = 1; i <= N; i += 1)
  96. Buffer.BlockCopy (R [i], 0, C, 8 * i, 8);
  97. }
  98. return C;
  99. }
  100. [MonoTODO]
  101. public static byte[] AESKeyWrapDecrypt (byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData)
  102. {
  103. SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael");
  104. symAlg.Mode = CipherMode.ECB;
  105. symAlg.Key = rgbKey;
  106. int N = ( rgbEncryptedWrappedKeyData.Length / 8 ) - 1;
  107. // From RFC 3394 - Advanced Encryption Standard (AES) Key Wrap Algorithm
  108. //
  109. // Inputs: Ciphertext, (n+1) 64-bit values (C0, C1, ..., Cn), and Key, K (the KEK)
  110. // Outputs: Plaintext, n 64-bit values (P1, P2, ..., Pn)
  111. //
  112. // 1. Initialize variables.
  113. // Set A = C[0]
  114. byte[] A = new byte [8];
  115. Buffer.BlockCopy (rgbEncryptedWrappedKeyData, 0, A, 0, 8);
  116. // For i = 1 to n
  117. // R[i] = C[i]
  118. byte[] R = new byte [N * 8];
  119. Buffer.BlockCopy (rgbEncryptedWrappedKeyData, 8, R, 0, rgbEncryptedWrappedKeyData.Length - 8);
  120. // 2. Compute intermediate values.
  121. // For j = 5 to 0
  122. // For i = n to 1
  123. // B = AES-1(K, (A^t) | R[i]) where t = n*j+i
  124. // A = MSB (64,B)
  125. // R[i] = LSB (64,B)
  126. ICryptoTransform transform = symAlg.CreateDecryptor ();
  127. for (int j = 5; j >= 0; j -= 1) {
  128. for (int i = N; i >= 1; i -= 1) {
  129. byte[] T = BitConverter.GetBytes ((long) N * j + i);
  130. if (BitConverter.IsLittleEndian)
  131. Array.Reverse (T);
  132. byte[] B = new Byte [16];
  133. byte[] r = new Byte [8];
  134. Buffer.BlockCopy (R, 8 * (i - 1), r, 0, 8);
  135. byte[] ciphertext = Concatenate (Xor (A, T), r);
  136. transform.TransformBlock (ciphertext, 0, 16, B, 0);
  137. A = MSB (B);
  138. Buffer.BlockCopy (LSB (B), 0, R, 8 * (i - 1), 8);
  139. }
  140. }
  141. // 3. Output results
  142. // If A is an appropriate initial value
  143. // Then
  144. // For i = 1 to n
  145. // P[i] = R[i]
  146. // Else
  147. // Return an error
  148. return R;
  149. }
  150. [MonoTODO]
  151. public static byte[] TripleDESKeyWrapEncrypt (byte[] rgbKey, byte[] rgbWrappedKeyData)
  152. {
  153. SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("TripleDES");
  154. // Algorithm from http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap
  155. // The following algorithm wraps (encrypts) a key (the wrapped key, WK) under a TRIPLEDES
  156. // key-encryption-key (KEK) as adopted from [CMS-Algorithms].
  157. // 1. Represent the key being wrapped as an octet sequence. If it is a TRIPLEDES key,
  158. // this is 24 octets (192 bits) with odd parity bit as the bottom bit of each octet.
  159. // rgbWrappedKeyData is the key being wrapped.
  160. // 2. Compute the CMS key checksum (Section 5.6.1) call this CKS.
  161. byte[] cks = ComputeCMSKeyChecksum (rgbWrappedKeyData);
  162. // 3. Let WKCKS = WK || CKS, where || is concatenation.
  163. byte[] wkcks = Concatenate (rgbWrappedKeyData, cks);
  164. // 4. Generate 8 random octets and call this IV.
  165. symAlg.GenerateIV ();
  166. // 5. Encrypt WKCKS in CBC mode using KEK as the key and IV as the initialization vector.
  167. // Call the results TEMP1.
  168. symAlg.Mode = CipherMode.CBC;
  169. symAlg.Padding = PaddingMode.None;
  170. symAlg.Key = rgbKey;
  171. byte[] temp1 = Transform (wkcks, symAlg.CreateEncryptor ());
  172. // 6. Let TEMP2 = IV || TEMP1.
  173. byte[] temp2 = Concatenate (symAlg.IV, temp1);
  174. // 7. Reverse the order of the octets in TEMP2 and call the result TEMP3.
  175. Array.Reverse (temp2); // TEMP3 is TEMP2
  176. // 8. Encrypt TEMP3 in CBC mode using the KEK and an initialization vector of 0x4adda22c79e82105.
  177. // The resulting cipher text is the desired result. It is 40 octets long if a 168 bit key
  178. // is being wrapped.
  179. symAlg.IV = new Byte [8] {0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05};
  180. byte[] rtnval = Transform (temp2, symAlg.CreateEncryptor ());
  181. return rtnval;
  182. }
  183. [MonoTODO]
  184. public static byte[] TripleDESKeyWrapDecrypt (byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData)
  185. {
  186. SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("TripleDES");
  187. // Algorithm from http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap
  188. // The following algorithm unwraps (decrypts) a key as adopted from [CMS-Algorithms].
  189. // 1. Check the length of the cipher text is reasonable given the key type. It must be
  190. // 40 bytes for a 168 bit key and either 32, 40, or 48 bytes for a 128, 192, or 256 bit
  191. // key. If the length is not supported or inconsistent with the algorithm for which the
  192. // key is intended, return error.
  193. // 2. Decrypt the cipher text with TRIPLEDES in CBC mode using the KEK and an initialization
  194. // vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
  195. symAlg.Mode = CipherMode.CBC;
  196. symAlg.Padding = PaddingMode.None;
  197. symAlg.Key = rgbKey;
  198. symAlg.IV = new Byte [8] {0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05};
  199. byte[] temp3 = Transform (rgbEncryptedWrappedKeyData, symAlg.CreateDecryptor ());
  200. // 3. Reverse the order of the octets in TEMP3 and call the result TEMP2.
  201. Array.Reverse (temp3); // TEMP2 is TEMP3.
  202. // 4. Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
  203. byte[] temp1 = new Byte [temp3.Length - 8];
  204. byte[] iv = new Byte [8];
  205. Buffer.BlockCopy (temp3, 0, iv, 0, 8);
  206. Buffer.BlockCopy (temp3, 8, temp1, 0, temp1.Length);
  207. // 5. Decrypt TEMP1 using TRIPLEDES in CBC mode using the KEK and the IV found in the previous step.
  208. // Call the result WKCKS.
  209. symAlg.IV = iv;
  210. byte[] wkcks = Transform (temp1, symAlg.CreateDecryptor ());
  211. // 6. Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are those octets before
  212. // the CKS.
  213. byte[] cks = new byte [8];
  214. byte[] wk = new byte [wkcks.Length - 8];
  215. Buffer.BlockCopy (wkcks, 0, wk, 0, wk.Length);
  216. Buffer.BlockCopy (wkcks, wk.Length, cks, 0, 8);
  217. // 7. Calculate the CMS key checksum over the WK and compare with the CKS extracted in the above
  218. // step. If they are not equal, return error.
  219. // 8. WK is the wrapped key, now extracted for use in data decryption.
  220. return wk;
  221. }
  222. private static byte[] Transform (byte[] data, ICryptoTransform t)
  223. {
  224. MemoryStream output = new MemoryStream ();
  225. CryptoStream crypto = new CryptoStream (output, t, CryptoStreamMode.Write);
  226. crypto.Write (data, 0, data.Length);
  227. crypto.FlushFinalBlock ();
  228. byte[] result = output.ToArray ();
  229. output.Close ();
  230. crypto.Close ();
  231. return result;
  232. }
  233. private static byte[] ComputeCMSKeyChecksum (byte[] data)
  234. {
  235. byte[] hash = HashAlgorithm.Create ("SHA1").ComputeHash (data);
  236. byte[] output = new byte [8];
  237. Buffer.BlockCopy (hash, 0, output, 0, 8);
  238. return output;
  239. }
  240. private static byte[] Concatenate (byte[] buf1, byte[] buf2)
  241. {
  242. byte[] output = new byte [buf1.Length + buf2.Length];
  243. Buffer.BlockCopy (buf1, 0, output, 0, buf1.Length);
  244. Buffer.BlockCopy (buf2, 0, output, buf1.Length, buf2.Length);
  245. return output;
  246. }
  247. private static byte[] MSB (byte[] input)
  248. {
  249. return MSB (input, 8);
  250. }
  251. private static byte[] MSB (byte[] input, int bytes)
  252. {
  253. byte[] output = new byte [bytes];
  254. Buffer.BlockCopy (input, 0, output, 0, bytes);
  255. return output;
  256. }
  257. private static byte[] LSB (byte[] input)
  258. {
  259. return LSB (input, 8);
  260. }
  261. private static byte[] LSB (byte[] input, int bytes)
  262. {
  263. byte[] output = new byte [bytes];
  264. Buffer.BlockCopy (input, bytes, output, 0, bytes);
  265. return output;
  266. }
  267. private static byte[] Xor (byte[] x, byte[] y)
  268. {
  269. // This should *not* happen.
  270. if (x.Length != y.Length)
  271. throw new CryptographicException ("Error performing Xor: arrays different length.");
  272. byte[] output = new byte [x.Length];
  273. for (int i = 0; i < x.Length; i += 1)
  274. output [i] = (byte) (x [i] ^ y [i]);
  275. return output;
  276. }
  277. private static byte[] Xor (byte[] x, int n)
  278. {
  279. byte[] output = new Byte [x.Length];
  280. for (int i = 0; i < x.Length; i += 1)
  281. output [i] = (byte) ((int) x [i] ^ n);
  282. return output;
  283. }
  284. }
  285. }
  286. #endif