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, 

25 key_size=key_size, 

26 backend=backend 

27 ) 

28 

29 

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

31 """ 

32 Returns private key PEM file bytes. 

33 :param private_key: RSPrivateKey 

34 :return: bytes 

35 """ 

36 return private_key.private_bytes( # type: ignore 

37 encoding=serialization.Encoding.PEM, 

38 format=serialization.PrivateFormat.PKCS8, 

39 encryption_algorithm=serialization.NoEncryption() 

40 ) 

41 

42 

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

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

45 return load_pem_private_key(pem_data, password=password, backend=backend) 

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('write_private_key_pem_file() assumes PEM data does contains BEGIN / END header and footer') 

61 with open(filename, 'wb') as fp: 

62 fp.write(key_base64) 

63 logger.info('%s written', filename) 

64 

65 

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

67 """ 

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

69 :param pem: bytes 

70 :return: bytes 

71 """ 

72 if not pem.startswith(b'-----BEGIN '): 

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

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

75 

76 

77def create_csr_pem(private_key: RSAPrivateKey, common_name: str, country_name: str, # pylint: disable=too-many-arguments,too-many-locals 

78 dn_qualifier: str = '', business_category: str = '', domain_component: str = '', email_address: str = '', 

79 generation_qualifier: str = '', given_name: str = '', jurisdiction_country_name: str = '', 

80 jurisdiction_locality_name: str = '', jurisdiction_state_or_province_name: str = '', 

81 locality_name: str = '', organizational_unit_name: str = '', organization_name: str = '', 

82 postal_address: str = '', postal_code: str = '', pseudonym: str = '', serial_number: str = '', 

83 state_or_province_name: str = '', street_address: str = '', surname: str = '', 

84 title: str = '', user_id: str = '', x500_unique_identifier: str = '') -> bytes: 

85 """ 

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

87 :return: CSR PEM as bytes 

88 """ 

89 pairs = [ 

90 (common_name, 'COMMON_NAME'), 

91 (country_name, 'COUNTRY_NAME'), 

92 (dn_qualifier, 'DN_QUALIFIER'), 

93 (business_category, 'BUSINESS_CATEGORY'), 

94 (domain_component, 'DOMAIN_COMPONENT'), 

95 (email_address, 'EMAIL_ADDRESS'), 

96 (generation_qualifier, 'GENERATION_QUALIFIER'), 

97 (given_name, 'GIVEN_NAME'), 

98 (jurisdiction_country_name, 'JURISDICTION_COUNTRY_NAME'), 

99 (jurisdiction_locality_name, 'JURISDICTION_LOCALITY_NAME'), 

100 (jurisdiction_state_or_province_name, 'JURISDICTION_STATE_OR_PROVINCE_NAME'), 

101 (locality_name, 'LOCALITY_NAME'), 

102 (organizational_unit_name, 'ORGANIZATIONAL_UNIT_NAME'), 

103 (organization_name, 'ORGANIZATION_NAME'), 

104 (postal_address, 'POSTAL_ADDRESS'), 

105 (postal_code, 'POSTAL_CODE'), 

106 (pseudonym, 'PSEUDONYM'), 

107 (serial_number, 'SERIAL_NUMBER'), 

108 (state_or_province_name, 'STATE_OR_PROVINCE_NAME'), 

109 (street_address, 'STREET_ADDRESS'), 

110 (surname, 'SURNAME'), 

111 (title, 'TITLE'), 

112 (user_id, 'USER_ID'), 

113 (x500_unique_identifier, 'X500_UNIQUE_IDENTIFIER'), 

114 ] 

115 name_parts = [] 

116 for val, k in pairs: 

117 if val: 

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

119 

120 builder = x509.CertificateSigningRequestBuilder() 

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

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

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

124 assert isinstance(request, x509.CertificateSigningRequest) 

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