PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/Passbook.Generator/PassGenerator.cs

https://github.com/Comezon/dotnet-passbook
C# | 405 lines | 342 code | 63 blank | 0 comment | 34 complexity | fa50d10acb84bc019b5dc4bd559e30e8 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.IO.Compression;
  7. using System.Linq;
  8. using System.Security.Cryptography;
  9. using System.Security.Cryptography.X509Certificates;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using Newtonsoft.Json;
  13. using Org.BouncyCastle.Cms;
  14. using Org.BouncyCastle.Security;
  15. using Org.BouncyCastle.X509.Store;
  16. using Passbook.Generator.Fields;
  17. namespace Passbook.Generator
  18. {
  19. public class PassGenerator
  20. {
  21. private byte[] passFile = null;
  22. private byte[] signatureFile = null;
  23. private byte[] manifestFile = null;
  24. private byte[] pkPassFile = null;
  25. private const string APPLE_CERTIFICATE_THUMBPRINT = "‎0950b6cd3d2f37ea246a1aaa20dfaadbd6fe1f75";
  26. public byte[] Generate(PassGeneratorRequest request)
  27. {
  28. if (request == null)
  29. {
  30. throw new ArgumentNullException("request", "You must pass an instance of PassGeneratorRequest");
  31. }
  32. CreatePackage(request);
  33. ZipPackage(request);
  34. return pkPassFile;
  35. }
  36. private void ZipPackage(PassGeneratorRequest request)
  37. {
  38. using (MemoryStream zipToOpen = new MemoryStream())
  39. {
  40. using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update, true))
  41. {
  42. ZipArchiveEntry imageEntry = null;
  43. if (request.Images.ContainsKey(PassbookImage.Icon))
  44. {
  45. imageEntry = archive.CreateEntry(@"icon.png");
  46. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  47. {
  48. writer.Write(request.Images[PassbookImage.Icon]);
  49. writer.Flush();
  50. }
  51. }
  52. if (request.Images.ContainsKey(PassbookImage.IconRetina))
  53. {
  54. imageEntry = archive.CreateEntry(@"icon@2x.png");
  55. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  56. {
  57. writer.Write(request.Images[PassbookImage.IconRetina]);
  58. writer.Flush();
  59. }
  60. }
  61. if (request.Images.ContainsKey(PassbookImage.Logo))
  62. {
  63. imageEntry = archive.CreateEntry(@"logo.png");
  64. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  65. {
  66. writer.Write(request.Images[PassbookImage.Logo]);
  67. writer.Flush();
  68. }
  69. }
  70. if (request.Images.ContainsKey(PassbookImage.LogoRetina))
  71. {
  72. imageEntry = archive.CreateEntry(@"logo@2x.png");
  73. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  74. {
  75. writer.Write(request.Images[PassbookImage.LogoRetina]);
  76. writer.Flush();
  77. }
  78. }
  79. if (request.Images.ContainsKey(PassbookImage.Background))
  80. {
  81. imageEntry = archive.CreateEntry(@"background.png");
  82. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  83. {
  84. writer.Write(request.Images[PassbookImage.Background]);
  85. writer.Flush();
  86. }
  87. }
  88. if (request.Images.ContainsKey(PassbookImage.BackgroundRetina))
  89. {
  90. imageEntry = archive.CreateEntry(@"background@2x.png");
  91. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  92. {
  93. writer.Write(request.Images[PassbookImage.BackgroundRetina]);
  94. writer.Flush();
  95. }
  96. }
  97. if (request.Images.ContainsKey(PassbookImage.Strip))
  98. {
  99. imageEntry = archive.CreateEntry(@"strip.png");
  100. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  101. {
  102. writer.Write(request.Images[PassbookImage.Strip]);
  103. writer.Flush();
  104. }
  105. }
  106. if (request.Images.ContainsKey(PassbookImage.StripRetina))
  107. {
  108. imageEntry = archive.CreateEntry(@"strip@2x.png");
  109. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  110. {
  111. writer.Write(request.Images[PassbookImage.StripRetina]);
  112. writer.Flush();
  113. }
  114. }
  115. if (request.Images.ContainsKey(PassbookImage.Thumbnail))
  116. {
  117. imageEntry = archive.CreateEntry(@"thumbnail.png");
  118. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  119. {
  120. writer.Write(request.Images[PassbookImage.Thumbnail]);
  121. writer.Flush();
  122. }
  123. }
  124. if (request.Images.ContainsKey(PassbookImage.ThumbnailRetina))
  125. {
  126. imageEntry = archive.CreateEntry(@"thumbnail@2x.png");
  127. using (BinaryWriter writer = new BinaryWriter(imageEntry.Open()))
  128. {
  129. writer.Write(request.Images[PassbookImage.ThumbnailRetina]);
  130. writer.Flush();
  131. }
  132. }
  133. ZipArchiveEntry PassJSONEntry = archive.CreateEntry(@"pass.json");
  134. using (BinaryWriter writer = new BinaryWriter(PassJSONEntry.Open()))
  135. {
  136. writer.Write(passFile);
  137. writer.Flush();
  138. }
  139. ZipArchiveEntry ManifestJSONEntry = archive.CreateEntry(@"manifest.json");
  140. using (BinaryWriter writer = new BinaryWriter(ManifestJSONEntry.Open()))
  141. {
  142. writer.Write(manifestFile);
  143. writer.Flush();
  144. }
  145. ZipArchiveEntry SignatureEntry = archive.CreateEntry(@"signature");
  146. using (BinaryWriter writer = new BinaryWriter(SignatureEntry.Open()))
  147. {
  148. writer.Write(signatureFile);
  149. writer.Flush();
  150. }
  151. }
  152. pkPassFile = zipToOpen.ToArray();
  153. zipToOpen.Flush();
  154. }
  155. }
  156. private void CreatePackage(PassGeneratorRequest request)
  157. {
  158. CreatePassFile(request);
  159. GenerateManifestFile(request);
  160. }
  161. private void CreatePassFile(PassGeneratorRequest request)
  162. {
  163. using (MemoryStream ms = new MemoryStream())
  164. {
  165. using (StreamWriter sr = new StreamWriter(ms))
  166. {
  167. using (JsonWriter writer = new JsonTextWriter(sr))
  168. {
  169. request.Write(writer);
  170. }
  171. passFile = ms.ToArray();
  172. }
  173. }
  174. }
  175. private void GenerateManifestFile(PassGeneratorRequest request)
  176. {
  177. using (MemoryStream ms = new MemoryStream())
  178. {
  179. using (StreamWriter sw = new StreamWriter(ms))
  180. {
  181. using (JsonWriter jsonWriter = new JsonTextWriter(sw))
  182. {
  183. jsonWriter.Formatting = Formatting.Indented;
  184. jsonWriter.WriteStartObject();
  185. string hash = null;
  186. if (request.Images.ContainsKey(PassbookImage.Icon))
  187. {
  188. hash = GetHashForBytes(request.Images[PassbookImage.Icon]);
  189. jsonWriter.WritePropertyName(@"icon.png");
  190. jsonWriter.WriteValue(hash.ToLower());
  191. }
  192. if (request.Images.ContainsKey(PassbookImage.IconRetina))
  193. {
  194. hash = GetHashForBytes(request.Images[PassbookImage.IconRetina]);
  195. jsonWriter.WritePropertyName(@"icon@2x.png");
  196. jsonWriter.WriteValue(hash.ToLower());
  197. }
  198. if (request.Images.ContainsKey(PassbookImage.Logo))
  199. {
  200. hash = GetHashForBytes(request.Images[PassbookImage.Logo]);
  201. jsonWriter.WritePropertyName(@"logo.png");
  202. jsonWriter.WriteValue(hash.ToLower());
  203. }
  204. if (request.Images.ContainsKey(PassbookImage.LogoRetina))
  205. {
  206. hash = GetHashForBytes(request.Images[PassbookImage.LogoRetina]);
  207. jsonWriter.WritePropertyName(@"logo@2x.png");
  208. jsonWriter.WriteValue(hash.ToLower());
  209. }
  210. if (request.Images.ContainsKey(PassbookImage.Background))
  211. {
  212. hash = GetHashForBytes(request.Images[PassbookImage.Background]);
  213. jsonWriter.WritePropertyName(@"background.png");
  214. jsonWriter.WriteValue(hash.ToLower());
  215. }
  216. if (request.Images.ContainsKey(PassbookImage.BackgroundRetina))
  217. {
  218. hash = GetHashForBytes(request.Images[PassbookImage.BackgroundRetina]);
  219. jsonWriter.WritePropertyName(@"background@2x.png");
  220. jsonWriter.WriteValue(hash.ToLower());
  221. }
  222. if (request.Images.ContainsKey(PassbookImage.Strip))
  223. {
  224. hash = GetHashForBytes(request.Images[PassbookImage.Strip]);
  225. jsonWriter.WritePropertyName(@"strip.png");
  226. jsonWriter.WriteValue(hash.ToLower());
  227. }
  228. if (request.Images.ContainsKey(PassbookImage.StripRetina))
  229. {
  230. hash = GetHashForBytes(request.Images[PassbookImage.StripRetina]);
  231. jsonWriter.WritePropertyName(@"strip@2x.png");
  232. jsonWriter.WriteValue(hash.ToLower());
  233. }
  234. if (request.Images.ContainsKey(PassbookImage.Thumbnail))
  235. {
  236. hash = GetHashForBytes(request.Images[PassbookImage.Thumbnail]);
  237. jsonWriter.WritePropertyName(@"thumbnail.png");
  238. jsonWriter.WriteValue(hash.ToLower());
  239. }
  240. if (request.Images.ContainsKey(PassbookImage.ThumbnailRetina))
  241. {
  242. hash = GetHashForBytes(request.Images[PassbookImage.ThumbnailRetina]);
  243. jsonWriter.WritePropertyName(@"thumbnail@2x.png");
  244. jsonWriter.WriteValue(hash.ToLower());
  245. }
  246. hash = GetHashForBytes(passFile);
  247. jsonWriter.WritePropertyName(@"pass.json");
  248. jsonWriter.WriteValue(hash.ToLower());
  249. }
  250. manifestFile = ms.ToArray();
  251. }
  252. SignManigestFile(request);
  253. }
  254. }
  255. private void SignManigestFile(PassGeneratorRequest request)
  256. {
  257. X509Certificate2 card = GetCertificate(request);
  258. if (card == null)
  259. {
  260. throw new FileNotFoundException("Certificate could not be found. Please ensure the thumbprint and cert location values are correct.");
  261. }
  262. Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(card);
  263. Org.BouncyCastle.Crypto.AsymmetricKeyParameter privateKey = DotNetUtilities.GetKeyPair(card.PrivateKey).Private;
  264. X509Certificate2 appleCA = GetAppleCertificate(request);
  265. Org.BouncyCastle.X509.X509Certificate appleCert = DotNetUtilities.FromX509Certificate(appleCA);
  266. ArrayList intermediateCerts = new ArrayList();
  267. intermediateCerts.Add(appleCert);
  268. intermediateCerts.Add(cert);
  269. Org.BouncyCastle.X509.Store.X509CollectionStoreParameters PP = new Org.BouncyCastle.X509.Store.X509CollectionStoreParameters(intermediateCerts);
  270. Org.BouncyCastle.X509.Store.IX509Store st1 = Org.BouncyCastle.X509.Store.X509StoreFactory.Create("CERTIFICATE/COLLECTION", PP);
  271. CmsSignedDataGenerator generator = new CmsSignedDataGenerator();
  272. generator.AddSigner(privateKey, cert, CmsSignedDataGenerator.DigestSha1);
  273. generator.AddCertificates(st1);
  274. CmsProcessable content = new CmsProcessableByteArray(manifestFile);
  275. CmsSignedData signedData = generator.Generate(content, false);
  276. signatureFile = signedData.GetEncoded();
  277. }
  278. private X509Certificate2 GetAppleCertificate(PassGeneratorRequest request)
  279. {
  280. if (request.AppleWWDRCACertificate == null)
  281. {
  282. return GetSpecifiedCertificateFromCertStore(APPLE_CERTIFICATE_THUMBPRINT, StoreName.CertificateAuthority, StoreLocation.LocalMachine);
  283. }
  284. else
  285. {
  286. return GetCertificateFromBytes(request.AppleWWDRCACertificate, null);
  287. }
  288. }
  289. public static X509Certificate2 GetCertificate(PassGeneratorRequest request)
  290. {
  291. if (request.Certificate == null)
  292. {
  293. return GetSpecifiedCertificateFromCertStore(request.CertThumbprint, StoreName.My, request.CertLocation);
  294. }
  295. else
  296. {
  297. return GetCertificateFromBytes(request.Certificate, request.CertificatePassword);
  298. }
  299. }
  300. private static X509Certificate2 GetSpecifiedCertificateFromCertStore(string thumbPrint, StoreName storeName, StoreLocation storeLocation)
  301. {
  302. X509Store store = new X509Store(storeName, storeLocation);
  303. store.Open(OpenFlags.ReadOnly);
  304. X509Certificate2Collection certs = store.Certificates;
  305. if (certs.Count > 0)
  306. {
  307. for (int i = 0; i < certs.Count; i++)
  308. {
  309. X509Certificate2 cert = certs[i];
  310. Debug.WriteLine(cert.Thumbprint);
  311. if (string.Compare(cert.Thumbprint, thumbPrint, true) == 0)
  312. {
  313. return certs[i];
  314. }
  315. }
  316. }
  317. return null;
  318. }
  319. private static X509Certificate2 GetCertificateFromBytes(byte[] bytes, string password)
  320. {
  321. X509Certificate2 certificate = null;
  322. if (password == null)
  323. {
  324. certificate = new X509Certificate2(bytes);
  325. }
  326. else
  327. {
  328. certificate = new X509Certificate2(bytes, password, X509KeyStorageFlags.Exportable);
  329. }
  330. return certificate;
  331. }
  332. private string GetHashForBytes(byte[] bytes)
  333. {
  334. SHA1CryptoServiceProvider oSHA1Hasher = new SHA1CryptoServiceProvider();
  335. byte[] hashBytes;
  336. hashBytes = oSHA1Hasher.ComputeHash(bytes);
  337. string hash = System.BitConverter.ToString(hashBytes);
  338. hash = hash.Replace("-", "");
  339. return hash;
  340. }
  341. }
  342. }