1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package pem implements the PEM data encoding, which originated in Privacy
6 // Enhanced Mail. The most common use of PEM encoding today is in TLS keys and
7 // certificates. See RFC 1421.
19 // A Block represents a PEM encoded structure.
21 // The encoded form is:
22 // -----BEGIN Type-----
24 // base64-encoded Bytes
26 // where Headers is a possibly empty sequence of Key: Value lines.
28 Type
string // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
29 Headers
map[string]string // Optional headers.
30 Bytes
[]byte // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.
33 // getLine results the first \r\n or \n delineated line from the given byte
34 // array. The line does not include trailing whitespace or the trailing new
35 // line bytes. The remainder of the byte array (also not including the new line
36 // bytes) is also returned and this will always be smaller than the original
38 func getLine(data
[]byte) (line
, rest
[]byte) {
39 i
:= bytes
.IndexByte(data
, '\n')
46 if i
> 0 && data
[i
-1] == '\r' {
50 return bytes
.TrimRight(data
[0:i
], " \t"), data
[j
:]
53 // removeWhitespace returns a copy of its input with all spaces, tab and
54 // newline characters removed.
55 func removeWhitespace(data
[]byte) []byte {
56 result
:= make([]byte, len(data
))
59 for _
, b
:= range data
{
60 if b
== ' ' || b
== '\t' || b
== '\r' || b
== '\n' {
70 var pemStart
= []byte("\n-----BEGIN ")
71 var pemEnd
= []byte("\n-----END ")
72 var pemEndOfLine
= []byte("-----")
74 // Decode will find the next PEM formatted block (certificate, private key
75 // etc) in the input. It returns that block and the remainder of the input. If
76 // no PEM data is found, p is nil and the whole of the input is returned in
78 func Decode(data
[]byte) (p
*Block
, rest
[]byte) {
79 // pemStart begins with a newline. However, at the very beginning of
80 // the byte array, we'll accept the start string without it.
82 if bytes
.HasPrefix(data
, pemStart
[1:]) {
83 rest
= rest
[len(pemStart
)-1 : len(data
)]
84 } else if i
:= bytes
.Index(data
, pemStart
); i
>= 0 {
85 rest
= rest
[i
+len(pemStart
) : len(data
)]
90 typeLine
, rest
:= getLine(rest
)
91 if !bytes
.HasSuffix(typeLine
, pemEndOfLine
) {
92 return decodeError(data
, rest
)
94 typeLine
= typeLine
[0 : len(typeLine
)-len(pemEndOfLine
)]
97 Headers
: make(map[string]string),
98 Type
: string(typeLine
),
102 // This loop terminates because getLine's second result is
103 // always smaller than its argument.
107 line
, next
:= getLine(rest
)
109 i
:= bytes
.IndexByte(line
, ':')
114 // TODO(agl): need to cope with values that spread across lines.
115 key
, val
:= line
[:i
], line
[i
+1:]
116 key
= bytes
.TrimSpace(key
)
117 val
= bytes
.TrimSpace(val
)
118 p
.Headers
[string(key
)] = string(val
)
122 var endIndex
, endTrailerIndex
int
124 // If there were no headers, the END line might occur
125 // immediately, without a leading newline.
126 if len(p
.Headers
) == 0 && bytes
.HasPrefix(rest
, pemEnd
[1:]) {
128 endTrailerIndex
= len(pemEnd
) - 1
130 endIndex
= bytes
.Index(rest
, pemEnd
)
131 endTrailerIndex
= endIndex
+ len(pemEnd
)
135 return decodeError(data
, rest
)
138 // After the "-----" of the ending line, there should be the same type
139 // and then a final five dashes.
140 endTrailer
:= rest
[endTrailerIndex
:]
141 endTrailerLen
:= len(typeLine
) + len(pemEndOfLine
)
142 if len(endTrailer
) < endTrailerLen
{
143 return decodeError(data
, rest
)
146 restOfEndLine
:= endTrailer
[endTrailerLen
:]
147 endTrailer
= endTrailer
[:endTrailerLen
]
148 if !bytes
.HasPrefix(endTrailer
, typeLine
) ||
149 !bytes
.HasSuffix(endTrailer
, pemEndOfLine
) {
150 return decodeError(data
, rest
)
153 // The line must end with only whitespace.
154 if s
, _
:= getLine(restOfEndLine
); len(s
) != 0 {
155 return decodeError(data
, rest
)
158 base64Data
:= removeWhitespace(rest
[:endIndex
])
159 p
.Bytes
= make([]byte, base64
.StdEncoding
.DecodedLen(len(base64Data
)))
160 n
, err
:= base64
.StdEncoding
.Decode(p
.Bytes
, base64Data
)
162 return decodeError(data
, rest
)
164 p
.Bytes
= p
.Bytes
[:n
]
166 // the -1 is because we might have only matched pemEnd without the
167 // leading newline if the PEM block was empty.
168 _
, rest
= getLine(rest
[endIndex
+len(pemEnd
)-1:])
173 func decodeError(data
, rest
[]byte) (*Block
, []byte) {
174 // If we get here then we have rejected a likely looking, but
175 // ultimately invalid PEM block. We need to start over from a new
176 // position. We have consumed the preamble line and will have consumed
177 // any lines which could be header lines. However, a valid preamble
178 // line is not a valid header line, therefore we cannot have consumed
179 // the preamble line for the any subsequent block. Thus, we will always
180 // find any valid block, no matter what bytes precede it.
182 // For example, if the input is
184 // -----BEGIN MALFORMED BLOCK-----
185 // junk that may look like header lines
186 // or data lines, but no END line
188 // -----BEGIN ACTUAL BLOCK-----
190 // -----END ACTUAL BLOCK-----
192 // we've failed to parse using the first BEGIN line
193 // and now will try again, using the second BEGIN line.
194 p
, rest
:= Decode(rest
)
201 const pemLineLength
= 64
203 type lineBreaker
struct {
204 line
[pemLineLength
]byte
209 var nl
= []byte{'\n'}
211 func (l
*lineBreaker
) Write(b
[]byte) (n
int, err error
) {
212 if l
.used
+len(b
) < pemLineLength
{
213 copy(l
.line
[l
.used
:], b
)
218 n
, err
= l
.out
.Write(l
.line
[0:l
.used
])
222 excess
:= pemLineLength
- l
.used
225 n
, err
= l
.out
.Write(b
[0:excess
])
230 n
, err
= l
.out
.Write(nl
)
235 return l
.Write(b
[excess
:])
238 func (l
*lineBreaker
) Close() (err error
) {
240 _
, err
= l
.out
.Write(l
.line
[0:l
.used
])
244 _
, err
= l
.out
.Write(nl
)
250 func writeHeader(out io
.Writer
, k
, v
string) error
{
251 _
, err
:= out
.Write([]byte(k
+ ": " + v
+ "\n"))
255 // Encode writes the PEM encoding of b to out.
256 func Encode(out io
.Writer
, b
*Block
) error
{
257 // Check for invalid block before writing any output.
258 for k
:= range b
.Headers
{
259 if strings
.Contains(k
, ":") {
260 return errors
.New("pem: cannot encode a header key that contains a colon")
264 // All errors below are relayed from underlying io.Writer,
265 // so it is now safe to write data.
267 if _
, err
:= out
.Write(pemStart
[1:]); err
!= nil {
270 if _
, err
:= out
.Write([]byte(b
.Type
+ "-----\n")); err
!= nil {
274 if len(b
.Headers
) > 0 {
275 const procType
= "Proc-Type"
276 h
:= make([]string, 0, len(b
.Headers
))
278 for k
:= range b
.Headers
{
285 // The Proc-Type header must be written first.
286 // See RFC 1421, section 4.6.1.1
288 if err
:= writeHeader(out
, procType
, b
.Headers
[procType
]); err
!= nil {
292 // For consistency of output, write other headers sorted by key.
294 for _
, k
:= range h
{
295 if err
:= writeHeader(out
, k
, b
.Headers
[k
]); err
!= nil {
299 if _
, err
:= out
.Write(nl
); err
!= nil {
304 var breaker lineBreaker
307 b64
:= base64
.NewEncoder(base64
.StdEncoding
, &breaker
)
308 if _
, err
:= b64
.Write(b
.Bytes
); err
!= nil {
314 if _
, err
:= out
.Write(pemEnd
[1:]); err
!= nil {
317 _
, err
:= out
.Write([]byte(b
.Type
+ "-----\n"))
321 // EncodeToMemory returns the PEM encoding of b.
323 // If b has invalid headers and cannot be encoded,
324 // EncodeToMemory returns nil. If it is important to
325 // report details about this error case, use Encode instead.
326 func EncodeToMemory(b
*Block
) []byte {
328 if err
:= Encode(&buf
, b
); err
!= nil {