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 // This package 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.
17 // A Block represents a PEM encoded structure.
19 // The encoded form is:
20 // -----BEGIN Type-----
22 // base64-encoded Bytes
24 // where Headers is a possibly empty sequence of Key: Value lines.
26 Type
string // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
27 Headers
map[string]string // Optional headers.
28 Bytes
[]byte // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.
31 // getLine results the first \r\n or \n delineated line from the given byte
32 // array. The line does not include the \r\n or \n. The remainder of the byte
33 // array (also not including the new line bytes) is also returned and this will
34 // always be smaller than the original argument.
35 func getLine(data
[]byte) (line
, rest
[]byte) {
36 i
:= bytes
.Index(data
, []byte{'\n'})
43 if i
> 0 && data
[i
-1] == '\r' {
47 return data
[0:i
], data
[j
:]
50 // removeWhitespace returns a copy of its input with all spaces, tab and
51 // newline characters removed.
52 func removeWhitespace(data
[]byte) []byte {
53 result
:= make([]byte, len(data
))
56 for _
, b
:= range data
{
57 if b
== ' ' || b
== '\t' || b
== '\r' || b
== '\n' {
67 var pemStart
= []byte("\n-----BEGIN ")
68 var pemEnd
= []byte("\n-----END ")
69 var pemEndOfLine
= []byte("-----")
71 // Decode will find the next PEM formatted block (certificate, private key
72 // etc) in the input. It returns that block and the remainder of the input. If
73 // no PEM data is found, p is nil and the whole of the input is returned in
75 func Decode(data
[]byte) (p
*Block
, rest
[]byte) {
76 // pemStart begins with a newline. However, at the very beginning of
77 // the byte array, we'll accept the start string without it.
79 if bytes
.HasPrefix(data
, pemStart
[1:]) {
80 rest
= rest
[len(pemStart
)-1 : len(data
)]
81 } else if i
:= bytes
.Index(data
, pemStart
); i
>= 0 {
82 rest
= rest
[i
+len(pemStart
) : len(data
)]
87 typeLine
, rest
:= getLine(rest
)
88 if !bytes
.HasSuffix(typeLine
, pemEndOfLine
) {
91 typeLine
= typeLine
[0 : len(typeLine
)-len(pemEndOfLine
)]
94 Headers
: make(map[string]string),
95 Type
: string(typeLine
),
99 // This loop terminates because getLine's second result is
100 // always smaller than it's argument.
104 line
, next
:= getLine(rest
)
106 i
:= bytes
.Index(line
, []byte{':'})
111 // TODO(agl): need to cope with values that spread across lines.
112 key
, val
:= line
[0:i
], line
[i
+1:]
113 key
= bytes
.TrimSpace(key
)
114 val
= bytes
.TrimSpace(val
)
115 p
.Headers
[string(key
)] = string(val
)
119 i
:= bytes
.Index(rest
, pemEnd
)
123 base64Data
:= removeWhitespace(rest
[0:i
])
125 p
.Bytes
= make([]byte, base64
.StdEncoding
.DecodedLen(len(base64Data
)))
126 n
, err
:= base64
.StdEncoding
.Decode(p
.Bytes
, base64Data
)
130 p
.Bytes
= p
.Bytes
[0:n
]
132 _
, rest
= getLine(rest
[i
+len(pemEnd
):])
137 // If we get here then we have rejected a likely looking, but
138 // ultimately invalid PEM block. We need to start over from a new
139 // position. We have consumed the preamble line and will have consumed
140 // any lines which could be header lines. However, a valid preamble
141 // line is not a valid header line, therefore we cannot have consumed
142 // the preamble line for the any subsequent block. Thus, we will always
143 // find any valid block, no matter what bytes preceed it.
145 // For example, if the input is
147 // -----BEGIN MALFORMED BLOCK-----
148 // junk that may look like header lines
149 // or data lines, but no END line
151 // -----BEGIN ACTUAL BLOCK-----
153 // -----END ACTUAL BLOCK-----
155 // we've failed to parse using the first BEGIN line
156 // and now will try again, using the second BEGIN line.
157 p
, rest
= Decode(rest
)
164 const pemLineLength
= 64
166 type lineBreaker
struct {
167 line
[pemLineLength
]byte
172 func (l
*lineBreaker
) Write(b
[]byte) (n
int, err os
.Error
) {
173 if l
.used
+len(b
) < pemLineLength
{
174 copy(l
.line
[l
.used
:], b
)
179 n
, err
= l
.out
.Write(l
.line
[0:l
.used
])
183 excess
:= pemLineLength
- l
.used
186 n
, err
= l
.out
.Write(b
[0:excess
])
191 n
, err
= l
.out
.Write([]byte{'\n'})
196 return l
.Write(b
[excess
:])
199 func (l
*lineBreaker
) Close() (err os
.Error
) {
201 _
, err
= l
.out
.Write(l
.line
[0:l
.used
])
205 _
, err
= l
.out
.Write([]byte{'\n'})
211 func Encode(out io
.Writer
, b
*Block
) (err os
.Error
) {
212 _
, err
= out
.Write(pemStart
[1:])
216 _
, err
= out
.Write([]byte(b
.Type
+ "-----\n"))
221 if len(b
.Headers
) > 0 {
222 for k
, v
:= range b
.Headers
{
223 _
, err
= out
.Write([]byte(k
+ ": " + v
+ "\n"))
228 _
, err
= out
.Write([]byte{'\n'})
234 var breaker lineBreaker
237 b64
:= base64
.NewEncoder(base64
.StdEncoding
, &breaker
)
238 _
, err
= b64
.Write(b
.Bytes
)
245 _
, err
= out
.Write(pemEnd
[1:])
249 _
, err
= out
.Write([]byte(b
.Type
+ "-----\n"))
253 func EncodeToMemory(b
*Block
) []byte {
254 buf
:= bytes
.NewBuffer(nil)