ocspclient.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env python
  2. #
  3. # This file is part of pyasn1-modules software.
  4. #
  5. # Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
  6. # License: http://snmplabs.com/pyasn1/license.html
  7. #
  8. import hashlib
  9. import sys
  10. try:
  11. import urllib2
  12. except ImportError:
  13. import urllib.request as urllib2
  14. from pyasn1.codec.der import decoder
  15. from pyasn1.codec.der import encoder
  16. from pyasn1.type import univ
  17. from pyasn1_modules import rfc2560
  18. from pyasn1_modules import rfc2459
  19. from pyasn1_modules import pem
  20. sha1oid = univ.ObjectIdentifier((1, 3, 14, 3, 2, 26))
  21. # noinspection PyClassHasNoInit
  22. class ValueOnlyBitStringEncoder(encoder.encoder.BitStringEncoder):
  23. # These methods just do not encode tag and length fields of TLV
  24. def encodeTag(self, *args):
  25. return ''
  26. def encodeLength(self, *args):
  27. return ''
  28. def encodeValue(*args):
  29. substrate, isConstructed = encoder.encoder.BitStringEncoder.encodeValue(*args)
  30. # OCSP-specific hack follows: cut off the "unused bit count"
  31. # encoded bit-string value.
  32. return substrate[1:], isConstructed
  33. def __call__(self, bitStringValue):
  34. return self.encode(None, bitStringValue, defMode=True, maxChunkSize=0)
  35. valueOnlyBitStringEncoder = ValueOnlyBitStringEncoder()
  36. # noinspection PyShadowingNames
  37. def mkOcspRequest(issuerCert, userCert):
  38. issuerTbsCertificate = issuerCert.getComponentByName('tbsCertificate')
  39. issuerSubject = issuerTbsCertificate.getComponentByName('subject')
  40. userTbsCertificate = userCert.getComponentByName('tbsCertificate')
  41. userIssuer = userTbsCertificate.getComponentByName('issuer')
  42. assert issuerSubject == userIssuer, '%s\n%s' % (
  43. issuerSubject.prettyPrint(), userIssuer.prettyPrint()
  44. )
  45. userIssuerHash = hashlib.sha1(
  46. encoder.encode(userIssuer)
  47. ).digest()
  48. issuerSubjectPublicKey = issuerTbsCertificate.getComponentByName('subjectPublicKeyInfo').getComponentByName(
  49. 'subjectPublicKey')
  50. issuerKeyHash = hashlib.sha1(
  51. valueOnlyBitStringEncoder(issuerSubjectPublicKey)
  52. ).digest()
  53. userSerialNumber = userTbsCertificate.getComponentByName('serialNumber')
  54. # Build request object
  55. request = rfc2560.Request()
  56. reqCert = request.setComponentByName('reqCert').getComponentByName('reqCert')
  57. hashAlgorithm = reqCert.setComponentByName('hashAlgorithm').getComponentByName('hashAlgorithm')
  58. hashAlgorithm.setComponentByName('algorithm', sha1oid)
  59. reqCert.setComponentByName('issuerNameHash', userIssuerHash)
  60. reqCert.setComponentByName('issuerKeyHash', issuerKeyHash)
  61. reqCert.setComponentByName('serialNumber', userSerialNumber)
  62. ocspRequest = rfc2560.OCSPRequest()
  63. tbsRequest = ocspRequest.setComponentByName('tbsRequest').getComponentByName('tbsRequest')
  64. tbsRequest.setComponentByName('version', 'v1')
  65. requestList = tbsRequest.setComponentByName('requestList').getComponentByName('requestList')
  66. requestList.setComponentByPosition(0, request)
  67. return ocspRequest
  68. def parseOcspResponse(ocspResponse):
  69. responseStatus = ocspResponse.getComponentByName('responseStatus')
  70. assert responseStatus == rfc2560.OCSPResponseStatus('successful'), responseStatus.prettyPrint()
  71. responseBytes = ocspResponse.getComponentByName('responseBytes')
  72. responseType = responseBytes.getComponentByName('responseType')
  73. assert responseType == rfc2560.id_pkix_ocsp_basic, responseType.prettyPrint()
  74. response = responseBytes.getComponentByName('response')
  75. basicOCSPResponse, _ = decoder.decode(
  76. response, asn1Spec=rfc2560.BasicOCSPResponse()
  77. )
  78. tbsResponseData = basicOCSPResponse.getComponentByName('tbsResponseData')
  79. response0 = tbsResponseData.getComponentByName('responses').getComponentByPosition(0)
  80. return (
  81. tbsResponseData.getComponentByName('producedAt'),
  82. response0.getComponentByName('certID'),
  83. response0.getComponentByName('certStatus').getName(),
  84. response0.getComponentByName('thisUpdate')
  85. )
  86. if len(sys.argv) != 2:
  87. print("""Usage:
  88. $ cat CACertificate.pem userCertificate.pem | %s <ocsp-responder-url>""" % sys.argv[0])
  89. sys.exit(-1)
  90. else:
  91. ocspUrl = sys.argv[1]
  92. # Parse CA and user certificates
  93. issuerCert, _ = decoder.decode(
  94. pem.readPemBlocksFromFile(
  95. sys.stdin, ('-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----')
  96. )[1],
  97. asn1Spec=rfc2459.Certificate()
  98. )
  99. # noinspection PyRedeclaration
  100. userCert, _ = decoder.decode(
  101. pem.readPemBlocksFromFile(
  102. sys.stdin, ('-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----')
  103. )[1],
  104. asn1Spec=rfc2459.Certificate()
  105. )
  106. # Build OCSP request
  107. ocspReq = mkOcspRequest(issuerCert, userCert)
  108. # Use HTTP POST to get response (see Appendix A of RFC 2560)
  109. # In case you need proxies, set the http_proxy env variable
  110. httpReq = urllib2.Request(
  111. ocspUrl,
  112. encoder.encode(ocspReq),
  113. {'Content-Type': 'application/ocsp-request'}
  114. )
  115. httpRsp = urllib2.urlopen(httpReq).read()
  116. # Process OCSP response
  117. # noinspection PyRedeclaration
  118. ocspRsp, _ = decoder.decode(httpRsp, asn1Spec=rfc2560.OCSPResponse())
  119. producedAt, certId, certStatus, thisUpdate = parseOcspResponse(ocspRsp)
  120. print('Certificate ID %s is %s at %s till %s\n' % (certId.getComponentByName('serialNumber'),
  121. certStatus, producedAt, thisUpdate))