make-sample-certs.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #!/usr/bin/env python3
  2. # Copyright 2022, gRPC Authors All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import argparse
  16. import datetime
  17. import os
  18. import shutil
  19. import subprocess
  20. import tempfile
  21. TEMPLATE = """\
  22. /*
  23. * Copyright {year}, gRPC Authors All rights reserved.
  24. *
  25. * Licensed under the Apache License, Version 2.0 (the "License");
  26. * you may not use this file except in compliance with the License.
  27. * You may obtain a copy of the License at
  28. *
  29. * http://www.apache.org/licenses/LICENSE-2.0
  30. *
  31. * Unless required by applicable law or agreed to in writing, software
  32. * distributed under the License is distributed on an "AS IS" BASIS,
  33. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  34. * See the License for the specific language governing permissions and
  35. * limitations under the License.
  36. */
  37. //-----------------------------------------------------------------------------
  38. // THIS FILE WAS GENERATED WITH make-sample-certs.py
  39. //
  40. // DO NOT UPDATE MANUALLY
  41. //-----------------------------------------------------------------------------
  42. #if canImport(NIOSSL)
  43. import struct Foundation.Date
  44. import NIOSSL
  45. /// Wraps `NIOSSLCertificate` to provide the certificate common name and expiry date.
  46. public struct SampleCertificate {{
  47. public var certificate: NIOSSLCertificate
  48. public var commonName: String
  49. public var notAfter: Date
  50. public static let ca = SampleCertificate(
  51. certificate: try! NIOSSLCertificate(bytes: .init(caCert.utf8), format: .pem),
  52. commonName: "some-ca",
  53. notAfter: Date(timeIntervalSince1970: {timestamp})
  54. )
  55. public static let otherCA = SampleCertificate(
  56. certificate: try! NIOSSLCertificate(bytes: .init(otherCACert.utf8), format: .pem),
  57. commonName: "some-other-ca",
  58. notAfter: Date(timeIntervalSince1970: {timestamp})
  59. )
  60. public static let server = SampleCertificate(
  61. certificate: try! NIOSSLCertificate(bytes: .init(serverCert.utf8), format: .pem),
  62. commonName: "localhost",
  63. notAfter: Date(timeIntervalSince1970: {timestamp})
  64. )
  65. public static let exampleServer = SampleCertificate(
  66. certificate: try! NIOSSLCertificate(bytes: .init(exampleServerCert.utf8), format: .pem),
  67. commonName: "example.com",
  68. notAfter: Date(timeIntervalSince1970: {timestamp})
  69. )
  70. public static let serverSignedByOtherCA = SampleCertificate(
  71. certificate: try! NIOSSLCertificate(bytes: .init(serverSignedByOtherCACert.utf8), format: .pem),
  72. commonName: "localhost",
  73. notAfter: Date(timeIntervalSince1970: {timestamp})
  74. )
  75. public static let client = SampleCertificate(
  76. certificate: try! NIOSSLCertificate(bytes: .init(clientCert.utf8), format: .pem),
  77. commonName: "localhost",
  78. notAfter: Date(timeIntervalSince1970: {timestamp})
  79. )
  80. public static let clientSignedByOtherCA = SampleCertificate(
  81. certificate: try! NIOSSLCertificate(bytes: .init(clientSignedByOtherCACert.utf8), format: .pem),
  82. commonName: "localhost",
  83. notAfter: Date(timeIntervalSince1970: {timestamp})
  84. )
  85. public static let exampleServerWithExplicitCurve = SampleCertificate(
  86. certificate: try! NIOSSLCertificate(bytes: .init(serverExplicitCurveCert.utf8), format: .pem),
  87. commonName: "localhost",
  88. notAfter: Date(timeIntervalSince1970: {timestamp})
  89. )
  90. }}
  91. extension SampleCertificate {{
  92. /// Returns whether the certificate has expired.
  93. public var isExpired: Bool {{
  94. return self.notAfter < Date()
  95. }}
  96. }}
  97. /// Provides convenience methods to make `NIOSSLPrivateKey`s for corresponding `GRPCSwiftCertificate`s.
  98. public struct SamplePrivateKey {{
  99. private init() {{}}
  100. public static let server = try! NIOSSLPrivateKey(bytes: .init(serverKey.utf8), format: .pem)
  101. public static let exampleServer = try! NIOSSLPrivateKey(
  102. bytes: .init(exampleServerKey.utf8),
  103. format: .pem
  104. )
  105. public static let client = try! NIOSSLPrivateKey(bytes: .init(clientKey.utf8), format: .pem)
  106. public static let exampleServerWithExplicitCurve = try! NIOSSLPrivateKey(
  107. bytes: .init(serverExplicitCurveKey.utf8),
  108. format: .pem
  109. )
  110. }}
  111. // MARK: - Certificates and private keys
  112. private let caCert = \"""
  113. {ca_cert}
  114. \"""
  115. private let otherCACert = \"""
  116. {other_ca_cert}
  117. \"""
  118. private let serverCert = \"""
  119. {server_cert}
  120. \"""
  121. private let serverSignedByOtherCACert = \"""
  122. {server_signed_by_other_ca_cert}
  123. \"""
  124. private let serverKey = \"""
  125. {server_key}
  126. \"""
  127. private let exampleServerCert = \"""
  128. {example_server_cert}
  129. \"""
  130. private let exampleServerKey = \"""
  131. {example_server_key}
  132. \"""
  133. private let clientCert = \"""
  134. {client_cert}
  135. \"""
  136. private let clientSignedByOtherCACert = \"""
  137. {client_signed_by_other_ca_cert}
  138. \"""
  139. private let clientKey = \"""
  140. {client_key}
  141. \"""
  142. private let serverExplicitCurveCert = \"""
  143. {server_explicit_curve_cert}
  144. \"""
  145. private let serverExplicitCurveKey = \"""
  146. {server_explicit_curve_key}
  147. \"""
  148. #endif // canImport(NIOSSL)
  149. """
  150. def load_file(root, name):
  151. with open(os.path.join(root, name)) as fh:
  152. return fh.read().strip()
  153. def extract_key(ec_key_and_params):
  154. lines = []
  155. include_line = True
  156. for line in ec_key_and_params.split("\n"):
  157. if line == "-----BEGIN EC PARAMETERS-----":
  158. include_line = False
  159. elif line == "-----BEGIN EC PRIVATE KEY-----":
  160. include_line = True
  161. if include_line:
  162. lines.append(line)
  163. return "\n".join(lines).strip()
  164. def update_sample_certs():
  165. now = datetime.datetime.now()
  166. # makecert uses an expiry of 365 days.
  167. delta = datetime.timedelta(days=365)
  168. # Seconds since epoch
  169. not_after = (now + delta).strftime("%s")
  170. # Expect to be called from the root of the checkout.
  171. root = os.path.abspath(os.curdir)
  172. executable = os.path.join(root, "scripts", "makecert")
  173. try:
  174. subprocess.check_call(executable)
  175. except FileNotFoundError:
  176. print("Please run the script from the root of the repository")
  177. exit(1)
  178. kwargs = {
  179. "year": now.year,
  180. "timestamp": not_after,
  181. "ca_cert": load_file(root, "ca.crt"),
  182. "other_ca_cert": load_file(root, "other-ca.crt"),
  183. "server_cert": load_file(root, "server-localhost.crt"),
  184. "server_signed_by_other_ca_cert": load_file(root, "server-localhost-other-ca.crt"),
  185. "server_key": load_file(root, "server-localhost.key"),
  186. "example_server_cert": load_file(root, "server-example.com.crt"),
  187. "example_server_key": load_file(root, "server-example.com.key"),
  188. "client_cert": load_file(root, "client.crt"),
  189. "client_signed_by_other_ca_cert": load_file(root, "client-other-ca.crt"),
  190. "client_key": load_file(root, "client.key"),
  191. "server_explicit_curve_cert": load_file(root, "server-explicit-ec.crt"),
  192. "server_explicit_curve_key": extract_key(load_file(root,
  193. "server-explicit-ec.key"))
  194. }
  195. formatted = TEMPLATE.format(**kwargs)
  196. with open("Sources/GRPCSampleData/GRPCSwiftCertificate.swift", "w") as fh:
  197. fh.write(formatted)
  198. def update_p12_bundle():
  199. tmp_dir = tempfile.TemporaryDirectory()
  200. subprocess.check_call(["git", "clone", "--single-branch", "--branch",
  201. "make-p12-bundle-for-grpc-swift-tests",
  202. "https://github.com/glbrntt/swift-nio-ssl",
  203. tmp_dir.name])
  204. subprocess.check_call(["./make-pkcs12.sh"], cwd=tmp_dir.name)
  205. shutil.copyfile(tmp_dir.name + "/bundle.p12", "Sources/GRPCSampleData/bundle.p12")
  206. if __name__ == "__main__":
  207. parser = argparse.ArgumentParser()
  208. parser.add_argument("--no-p12-bundle", action="store_false", dest="p12")
  209. parser.add_argument("--no-sample-certs", action="store_false", dest="sample_certs")
  210. args = parser.parse_args()
  211. if args.sample_certs:
  212. update_sample_certs()
  213. if args.p12:
  214. update_p12_bundle()