Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import logging 

2from typing import Optional 

3 

4import cryptography 

5from cryptography import x509 

6from cryptography.hazmat.primitives import hashes, serialization 

7from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey 

8from cryptography.hazmat.primitives.serialization import load_pem_private_key 

9from django.core.exceptions import ValidationError 

10 

11 

12logger = logging.getLogger(__name__) 

13 

14 

15def create_private_key(public_exponent: int = 65537, key_size: int = 2048) -> RSAPrivateKey: 

16 """ 

17 Creates RSA private key. 

18 :param public_exponent: int, exponent 

19 :param key_size: int, bits 

20 :return: RSAPrivateKey 

21 """ 

22 backend = cryptography.hazmat.backends.default_backend() 

23 return cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key( 

24 public_exponent=public_exponent, key_size=key_size, backend=backend 

25 ) 

26 

27 

28def get_private_key_pem(private_key: RSAPrivateKey) -> bytes: 

29 """ 

30 Returns private key PEM file bytes. 

31 :param private_key: RSPrivateKey 

32 :return: bytes 

33 """ 

34 return private_key.private_bytes( # type: ignore 

35 encoding=serialization.Encoding.PEM, 

36 format=serialization.PrivateFormat.PKCS8, 

37 encryption_algorithm=serialization.NoEncryption(), 

38 ) 

39 

40 

41def load_private_key_from_pem_data(pem_data: bytes, password: Optional[bytes] = None) -> RSAPrivateKey: 

42 backend = cryptography.hazmat.backends.default_backend() 

43 res = load_pem_private_key(pem_data, password=password, backend=backend) 

44 assert isinstance(res, RSAPrivateKey) 

45 return res 

46 

47 

48def load_private_key_from_pem_file(filename: str, password: Optional[bytes] = None) -> RSAPrivateKey: 

49 with open(filename, "rb") as fp: 

50 return load_private_key_from_pem_data(fp.read(), password) 

51 

52 

53def write_private_key_pem_file(filename: str, key_base64: bytes): 

54 """ 

55 Writes PEM data to file. 

56 :param filename: PEM filename 

57 :param cert_base64: Base64 encoded certificate data without BEGIN CERTIFICATE / END CERTIFICATE 

58 """ 

59 if b"BEGIN" not in key_base64 or b"END" not in key_base64: 

60 raise ValidationError( 

61 "write_private_key_pem_file() assumes PEM data does contains BEGIN / END header and footer" 

62 ) 

63 with open(filename, "wb") as fp: 

64 fp.write(key_base64) 

65 logger.info("%s written", filename) 

66 

67 

68def strip_pem_header_and_footer(pem: bytes) -> bytes: 

69 """ 

70 Strips -----BEGIN and -----END parts of the CSR PEM. 

71 :param pem: bytes 

72 :return: bytes 

73 """ 

74 if not pem.startswith(b"-----BEGIN "): 

75 raise Exception("PEM does not appear to have header: {}...".format(pem[:32].decode() + "...")) 

76 return b"\n".join(pem.split(b"\n")[1:-2]) 

77 

78 

79def create_csr_pem( # pylint: disable=too-many-arguments,too-many-locals 

80 private_key: RSAPrivateKey, 

81 common_name: str, 

82 country_name: str, 

83 dn_qualifier: str = "", 

84 business_category: str = "", 

85 domain_component: str = "", 

86 email_address: str = "", 

87 generation_qualifier: str = "", 

88 given_name: str = "", 

89 jurisdiction_country_name: str = "", 

90 jurisdiction_locality_name: str = "", 

91 jurisdiction_state_or_province_name: str = "", 

92 locality_name: str = "", 

93 organizational_unit_name: str = "", 

94 organization_name: str = "", 

95 postal_address: str = "", 

96 postal_code: str = "", 

97 pseudonym: str = "", 

98 serial_number: str = "", 

99 state_or_province_name: str = "", 

100 street_address: str = "", 

101 surname: str = "", 

102 title: str = "", 

103 user_id: str = "", 

104 x500_unique_identifier: str = "", 

105) -> bytes: 

106 """ 

107 See http://fileformats.archiveteam.org/wiki/PKCS10 

108 :return: CSR PEM as bytes 

109 """ 

110 pairs = [ 

111 (common_name, "COMMON_NAME"), 

112 (country_name, "COUNTRY_NAME"), 

113 (dn_qualifier, "DN_QUALIFIER"), 

114 (business_category, "BUSINESS_CATEGORY"), 

115 (domain_component, "DOMAIN_COMPONENT"), 

116 (email_address, "EMAIL_ADDRESS"), 

117 (generation_qualifier, "GENERATION_QUALIFIER"), 

118 (given_name, "GIVEN_NAME"), 

119 (jurisdiction_country_name, "JURISDICTION_COUNTRY_NAME"), 

120 (jurisdiction_locality_name, "JURISDICTION_LOCALITY_NAME"), 

121 (jurisdiction_state_or_province_name, "JURISDICTION_STATE_OR_PROVINCE_NAME"), 

122 (locality_name, "LOCALITY_NAME"), 

123 (organizational_unit_name, "ORGANIZATIONAL_UNIT_NAME"), 

124 (organization_name, "ORGANIZATION_NAME"), 

125 (postal_address, "POSTAL_ADDRESS"), 

126 (postal_code, "POSTAL_CODE"), 

127 (pseudonym, "PSEUDONYM"), 

128 (serial_number, "SERIAL_NUMBER"), 

129 (state_or_province_name, "STATE_OR_PROVINCE_NAME"), 

130 (street_address, "STREET_ADDRESS"), 

131 (surname, "SURNAME"), 

132 (title, "TITLE"), 

133 (user_id, "USER_ID"), 

134 (x500_unique_identifier, "X500_UNIQUE_IDENTIFIER"), 

135 ] 

136 name_parts = [] 

137 for val, k in pairs: 

138 if val: 

139 name_parts.append(x509.NameAttribute(getattr(x509.oid.NameOID, k), val)) 

140 

141 builder = x509.CertificateSigningRequestBuilder() 

142 builder = builder.subject_name(x509.Name(name_parts)) 

143 backend = cryptography.hazmat.backends.default_backend() 

144 request = builder.sign(private_key, hashes.SHA256(), backend) 

145 assert isinstance(request, x509.CertificateSigningRequest) 

146 return request.public_bytes(serialization.Encoding.PEM)