• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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