DesEdeWrapEngine.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  9. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines
  10. {
  11. /**
  12. * Wrap keys according to
  13. * <a href="http://www.ietf.org/internet-drafts/draft-ietf-smime-key-wrap-01.txt">
  14. * draft-ietf-smime-key-wrap-01.txt</a>.
  15. * <p>
  16. * Note:
  17. * <ul>
  18. * <li>this is based on a draft, and as such is subject to change - don't use this class for anything requiring long term storage.</li>
  19. * <li>if you are using this to wrap triple-des keys you need to set the
  20. * parity bits on the key and, if it's a two-key triple-des key, pad it
  21. * yourself.</li>
  22. * </ul>
  23. * </p>
  24. */
  25. public class DesEdeWrapEngine
  26. : IWrapper
  27. {
  28. /** Field engine */
  29. private CbcBlockCipher engine;
  30. /** Field param */
  31. private KeyParameter param;
  32. /** Field paramPlusIV */
  33. private ParametersWithIV paramPlusIV;
  34. /** Field iv */
  35. private byte[] iv;
  36. /** Field forWrapping */
  37. private bool forWrapping;
  38. /** Field IV2 */
  39. private static readonly byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
  40. (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
  41. (byte) 0x21, (byte) 0x05 };
  42. //
  43. // checksum digest
  44. //
  45. private readonly IDigest sha1 = new Sha1Digest();
  46. private readonly byte[] digest = new byte[20];
  47. /**
  48. * Method init
  49. *
  50. * @param forWrapping
  51. * @param param
  52. */
  53. public virtual void Init(
  54. bool forWrapping,
  55. ICipherParameters parameters)
  56. {
  57. this.forWrapping = forWrapping;
  58. this.engine = new CbcBlockCipher(new DesEdeEngine());
  59. SecureRandom sr;
  60. if (parameters is ParametersWithRandom)
  61. {
  62. ParametersWithRandom pr = (ParametersWithRandom) parameters;
  63. parameters = pr.Parameters;
  64. sr = pr.Random;
  65. }
  66. else
  67. {
  68. sr = new SecureRandom();
  69. }
  70. if (parameters is KeyParameter)
  71. {
  72. this.param = (KeyParameter) parameters;
  73. if (this.forWrapping)
  74. {
  75. // Hm, we have no IV but we want to wrap ?!?
  76. // well, then we have to create our own IV.
  77. this.iv = new byte[8];
  78. sr.NextBytes(iv);
  79. this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
  80. }
  81. }
  82. else if (parameters is ParametersWithIV)
  83. {
  84. if (!forWrapping)
  85. throw new ArgumentException("You should not supply an IV for unwrapping");
  86. this.paramPlusIV = (ParametersWithIV) parameters;
  87. this.iv = this.paramPlusIV.GetIV();
  88. this.param = (KeyParameter) this.paramPlusIV.Parameters;
  89. if (this.iv.Length != 8)
  90. throw new ArgumentException("IV is not 8 octets", "parameters");
  91. }
  92. }
  93. /**
  94. * Method GetAlgorithmName
  95. *
  96. * @return
  97. */
  98. public virtual string AlgorithmName
  99. {
  100. get { return "DESede"; }
  101. }
  102. /**
  103. * Method wrap
  104. *
  105. * @param in
  106. * @param inOff
  107. * @param inLen
  108. * @return
  109. */
  110. public virtual byte[] Wrap(
  111. byte[] input,
  112. int inOff,
  113. int length)
  114. {
  115. if (!forWrapping)
  116. {
  117. throw new InvalidOperationException("Not initialized for wrapping");
  118. }
  119. byte[] keyToBeWrapped = new byte[length];
  120. Array.Copy(input, inOff, keyToBeWrapped, 0, length);
  121. // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
  122. byte[] CKS = CalculateCmsKeyChecksum(keyToBeWrapped);
  123. // Let WKCKS = WK || CKS where || is concatenation.
  124. byte[] WKCKS = new byte[keyToBeWrapped.Length + CKS.Length];
  125. Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length);
  126. Array.Copy(CKS, 0, WKCKS, keyToBeWrapped.Length, CKS.Length);
  127. // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
  128. // initialization vector. Call the results TEMP1.
  129. int blockSize = engine.GetBlockSize();
  130. if (WKCKS.Length % blockSize != 0)
  131. throw new InvalidOperationException("Not multiple of block length");
  132. engine.Init(true, paramPlusIV);
  133. byte [] TEMP1 = new byte[WKCKS.Length];
  134. for (int currentBytePos = 0; currentBytePos != WKCKS.Length; currentBytePos += blockSize)
  135. {
  136. engine.ProcessBlock(WKCKS, currentBytePos, TEMP1, currentBytePos);
  137. }
  138. // Let TEMP2 = IV || TEMP1.
  139. byte[] TEMP2 = new byte[this.iv.Length + TEMP1.Length];
  140. Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length);
  141. Array.Copy(TEMP1, 0, TEMP2, this.iv.Length, TEMP1.Length);
  142. // Reverse the order of the octets in TEMP2 and call the result TEMP3.
  143. byte[] TEMP3 = reverse(TEMP2);
  144. // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
  145. // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
  146. // result. It is 40 octets long if a 168 bit key is being wrapped.
  147. ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
  148. this.engine.Init(true, param2);
  149. for (int currentBytePos = 0; currentBytePos != TEMP3.Length; currentBytePos += blockSize)
  150. {
  151. engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
  152. }
  153. return TEMP3;
  154. }
  155. /**
  156. * Method unwrap
  157. *
  158. * @param in
  159. * @param inOff
  160. * @param inLen
  161. * @return
  162. * @throws InvalidCipherTextException
  163. */
  164. public virtual byte[] Unwrap(
  165. byte[] input,
  166. int inOff,
  167. int length)
  168. {
  169. if (forWrapping)
  170. {
  171. throw new InvalidOperationException("Not set for unwrapping");
  172. }
  173. if (input == null)
  174. {
  175. throw new InvalidCipherTextException("Null pointer as ciphertext");
  176. }
  177. int blockSize = engine.GetBlockSize();
  178. if (length % blockSize != 0)
  179. {
  180. throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize);
  181. }
  182. /*
  183. // Check if the length of the cipher text is reasonable given the key
  184. // type. It must be 40 bytes for a 168 bit key and either 32, 40, or
  185. // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported
  186. // or inconsistent with the algorithm for which the key is intended,
  187. // return error.
  188. //
  189. // we do not accept 168 bit keys. it has to be 192 bit.
  190. int lengthA = (estimatedKeyLengthInBit / 8) + 16;
  191. int lengthB = estimatedKeyLengthInBit % 8;
  192. if ((lengthA != keyToBeUnwrapped.Length) || (lengthB != 0)) {
  193. throw new XMLSecurityException("empty");
  194. }
  195. */
  196. // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
  197. // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
  198. ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
  199. this.engine.Init(false, param2);
  200. byte [] TEMP3 = new byte[length];
  201. for (int currentBytePos = 0; currentBytePos != TEMP3.Length; currentBytePos += blockSize)
  202. {
  203. engine.ProcessBlock(input, inOff + currentBytePos, TEMP3, currentBytePos);
  204. }
  205. // Reverse the order of the octets in TEMP3 and call the result TEMP2.
  206. byte[] TEMP2 = reverse(TEMP3);
  207. // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
  208. this.iv = new byte[8];
  209. byte[] TEMP1 = new byte[TEMP2.Length - 8];
  210. Array.Copy(TEMP2, 0, this.iv, 0, 8);
  211. Array.Copy(TEMP2, 8, TEMP1, 0, TEMP2.Length - 8);
  212. // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
  213. // found in the previous step. Call the result WKCKS.
  214. this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
  215. this.engine.Init(false, this.paramPlusIV);
  216. byte[] WKCKS = new byte[TEMP1.Length];
  217. for (int currentBytePos = 0; currentBytePos != WKCKS.Length; currentBytePos += blockSize)
  218. {
  219. engine.ProcessBlock(TEMP1, currentBytePos, WKCKS, currentBytePos);
  220. }
  221. // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are
  222. // those octets before the CKS.
  223. byte[] result = new byte[WKCKS.Length - 8];
  224. byte[] CKStoBeVerified = new byte[8];
  225. Array.Copy(WKCKS, 0, result, 0, WKCKS.Length - 8);
  226. Array.Copy(WKCKS, WKCKS.Length - 8, CKStoBeVerified, 0, 8);
  227. // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare
  228. // with the CKS extracted in the above step. If they are not equal, return error.
  229. if (!CheckCmsKeyChecksum(result, CKStoBeVerified)) {
  230. throw new InvalidCipherTextException(
  231. "Checksum inside ciphertext is corrupted");
  232. }
  233. // WK is the wrapped key, now extracted for use in data decryption.
  234. return result;
  235. }
  236. /**
  237. * Some key wrap algorithms make use of the Key Checksum defined
  238. * in CMS [CMS-Algorithms]. This is used to provide an integrity
  239. * check value for the key being wrapped. The algorithm is
  240. *
  241. * - Compute the 20 octet SHA-1 hash on the key being wrapped.
  242. * - Use the first 8 octets of this hash as the checksum value.
  243. *
  244. * @param key
  245. * @return
  246. * @throws Exception
  247. * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
  248. */
  249. private byte[] CalculateCmsKeyChecksum(
  250. byte[] key)
  251. {
  252. sha1.BlockUpdate(key, 0, key.Length);
  253. sha1.DoFinal(digest, 0);
  254. byte[] result = new byte[8];
  255. Array.Copy(digest, 0, result, 0, 8);
  256. return result;
  257. }
  258. /**
  259. * @param key
  260. * @param checksum
  261. * @return
  262. * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
  263. */
  264. private bool CheckCmsKeyChecksum(
  265. byte[] key,
  266. byte[] checksum)
  267. {
  268. return Arrays.ConstantTimeAreEqual(CalculateCmsKeyChecksum(key), checksum);
  269. }
  270. private static byte[] reverse(byte[] bs)
  271. {
  272. byte[] result = new byte[bs.Length];
  273. for (int i = 0; i < bs.Length; i++)
  274. {
  275. result[i] = bs[bs.Length - (i + 1)];
  276. }
  277. return result;
  278. }
  279. }
  280. }
  281. #pragma warning restore
  282. #endif