1# -*- coding: utf-8 -*- 2# 3# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17'''Functions that load and write PEM-encoded files.''' 18 19import base64 20from rsa._compat import b, is_bytes 21 22def _markers(pem_marker): 23 ''' 24 Returns the start and end PEM markers 25 ''' 26 27 if is_bytes(pem_marker): 28 pem_marker = pem_marker.decode('utf-8') 29 30 return (b('-----BEGIN %s-----' % pem_marker), 31 b('-----END %s-----' % pem_marker)) 32 33def load_pem(contents, pem_marker): 34 '''Loads a PEM file. 35 36 @param contents: the contents of the file to interpret 37 @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' 38 when your file has '-----BEGIN RSA PRIVATE KEY-----' and 39 '-----END RSA PRIVATE KEY-----' markers. 40 41 @return the base64-decoded content between the start and end markers. 42 43 @raise ValueError: when the content is invalid, for example when the start 44 marker cannot be found. 45 46 ''' 47 48 (pem_start, pem_end) = _markers(pem_marker) 49 50 pem_lines = [] 51 in_pem_part = False 52 53 for line in contents.splitlines(): 54 line = line.strip() 55 56 # Skip empty lines 57 if not line: 58 continue 59 60 # Handle start marker 61 if line == pem_start: 62 if in_pem_part: 63 raise ValueError('Seen start marker "%s" twice' % pem_start) 64 65 in_pem_part = True 66 continue 67 68 # Skip stuff before first marker 69 if not in_pem_part: 70 continue 71 72 # Handle end marker 73 if in_pem_part and line == pem_end: 74 in_pem_part = False 75 break 76 77 # Load fields 78 if b(':') in line: 79 continue 80 81 pem_lines.append(line) 82 83 # Do some sanity checks 84 if not pem_lines: 85 raise ValueError('No PEM start marker "%s" found' % pem_start) 86 87 if in_pem_part: 88 raise ValueError('No PEM end marker "%s" found' % pem_end) 89 90 # Base64-decode the contents 91 pem = b('').join(pem_lines) 92 return base64.decodestring(pem) 93 94 95def save_pem(contents, pem_marker): 96 '''Saves a PEM file. 97 98 @param contents: the contents to encode in PEM format 99 @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' 100 when your file has '-----BEGIN RSA PRIVATE KEY-----' and 101 '-----END RSA PRIVATE KEY-----' markers. 102 103 @return the base64-encoded content between the start and end markers. 104 105 ''' 106 107 (pem_start, pem_end) = _markers(pem_marker) 108 109 b64 = base64.encodestring(contents).replace(b('\n'), b('')) 110 pem_lines = [pem_start] 111 112 for block_start in range(0, len(b64), 64): 113 block = b64[block_start:block_start + 64] 114 pem_lines.append(block) 115 116 pem_lines.append(pem_end) 117 pem_lines.append(b('')) 118 119 return b('\n').join(pem_lines) 120 121