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. 4 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. 8 package pem 9 10 import ( 11 "bytes" 12 "encoding/base64" 13 "errors" 14 "io" 15 "sort" 16 "strings" 17 ) 18 19 // A Block represents a PEM encoded structure. 20 // 21 // The encoded form is: 22 // -----BEGIN Type----- 23 // Headers 24 // base64-encoded Bytes 25 // -----END Type----- 26 // where Headers is a possibly empty sequence of Key: Value lines. 27 type Block struct { 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. 31 } 32 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 37 // argument. 38 func getLine(data []byte) (line, rest []byte) { 39 i := bytes.Index(data, []byte{'\n'}) 40 var j int 41 if i < 0 { 42 i = len(data) 43 j = i 44 } else { 45 j = i + 1 46 if i > 0 && data[i-1] == '\r' { 47 i-- 48 } 49 } 50 return bytes.TrimRight(data[0:i], " \t"), data[j:] 51 } 52 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)) 57 n := 0 58 59 for _, b := range data { 60 if b == ' ' || b == '\t' || b == '\r' || b == '\n' { 61 continue 62 } 63 result[n] = b 64 n++ 65 } 66 67 return result[0:n] 68 } 69 70 var pemStart = []byte("\n-----BEGIN ") 71 var pemEnd = []byte("\n-----END ") 72 var pemEndOfLine = []byte("-----") 73 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 77 // rest. 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. 81 rest = data 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)] 86 } else { 87 return nil, data 88 } 89 90 typeLine, rest := getLine(rest) 91 if !bytes.HasSuffix(typeLine, pemEndOfLine) { 92 return decodeError(data, rest) 93 } 94 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] 95 96 p = &Block{ 97 Headers: make(map[string]string), 98 Type: string(typeLine), 99 } 100 101 for { 102 // This loop terminates because getLine's second result is 103 // always smaller than its argument. 104 if len(rest) == 0 { 105 return nil, data 106 } 107 line, next := getLine(rest) 108 109 i := bytes.Index(line, []byte{':'}) 110 if i == -1 { 111 break 112 } 113 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) 119 rest = next 120 } 121 122 var endIndex int 123 // If there were no headers, the END line might occur 124 // immediately, without a leading newline. 125 if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) { 126 endIndex = 0 127 } else { 128 endIndex = bytes.Index(rest, pemEnd) 129 } 130 131 if endIndex < 0 { 132 return decodeError(data, rest) 133 } 134 135 base64Data := removeWhitespace(rest[:endIndex]) 136 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) 137 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) 138 if err != nil { 139 return decodeError(data, rest) 140 } 141 p.Bytes = p.Bytes[:n] 142 143 // the -1 is because we might have only matched pemEnd without the 144 // leading newline if the PEM block was empty. 145 _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) 146 147 return 148 } 149 150 func decodeError(data, rest []byte) (*Block, []byte) { 151 // If we get here then we have rejected a likely looking, but 152 // ultimately invalid PEM block. We need to start over from a new 153 // position. We have consumed the preamble line and will have consumed 154 // any lines which could be header lines. However, a valid preamble 155 // line is not a valid header line, therefore we cannot have consumed 156 // the preamble line for the any subsequent block. Thus, we will always 157 // find any valid block, no matter what bytes precede it. 158 // 159 // For example, if the input is 160 // 161 // -----BEGIN MALFORMED BLOCK----- 162 // junk that may look like header lines 163 // or data lines, but no END line 164 // 165 // -----BEGIN ACTUAL BLOCK----- 166 // realdata 167 // -----END ACTUAL BLOCK----- 168 // 169 // we've failed to parse using the first BEGIN line 170 // and now will try again, using the second BEGIN line. 171 p, rest := Decode(rest) 172 if p == nil { 173 rest = data 174 } 175 return p, rest 176 } 177 178 const pemLineLength = 64 179 180 type lineBreaker struct { 181 line [pemLineLength]byte 182 used int 183 out io.Writer 184 } 185 186 var nl = []byte{'\n'} 187 188 func (l *lineBreaker) Write(b []byte) (n int, err error) { 189 if l.used+len(b) < pemLineLength { 190 copy(l.line[l.used:], b) 191 l.used += len(b) 192 return len(b), nil 193 } 194 195 n, err = l.out.Write(l.line[0:l.used]) 196 if err != nil { 197 return 198 } 199 excess := pemLineLength - l.used 200 l.used = 0 201 202 n, err = l.out.Write(b[0:excess]) 203 if err != nil { 204 return 205 } 206 207 n, err = l.out.Write(nl) 208 if err != nil { 209 return 210 } 211 212 return l.Write(b[excess:]) 213 } 214 215 func (l *lineBreaker) Close() (err error) { 216 if l.used > 0 { 217 _, err = l.out.Write(l.line[0:l.used]) 218 if err != nil { 219 return 220 } 221 _, err = l.out.Write(nl) 222 } 223 224 return 225 } 226 227 func writeHeader(out io.Writer, k, v string) error { 228 _, err := out.Write([]byte(k + ": " + v + "\n")) 229 return err 230 } 231 232 func Encode(out io.Writer, b *Block) error { 233 if _, err := out.Write(pemStart[1:]); err != nil { 234 return err 235 } 236 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { 237 return err 238 } 239 240 if len(b.Headers) > 0 { 241 const procType = "Proc-Type" 242 h := make([]string, 0, len(b.Headers)) 243 hasProcType := false 244 for k := range b.Headers { 245 if k == procType { 246 hasProcType = true 247 continue 248 } 249 h = append(h, k) 250 } 251 // The Proc-Type header must be written first. 252 // See RFC 1421, section 4.6.1.1 253 if hasProcType { 254 if err := writeHeader(out, procType, b.Headers[procType]); err != nil { 255 return err 256 } 257 } 258 // For consistency of output, write other headers sorted by key. 259 sort.Strings(h) 260 for _, k := range h { 261 if strings.Contains(k, ":") { 262 return errors.New("pem: cannot encode a header key that contains a colon") 263 } 264 if err := writeHeader(out, k, b.Headers[k]); err != nil { 265 return err 266 } 267 } 268 if _, err := out.Write(nl); err != nil { 269 return err 270 } 271 } 272 273 var breaker lineBreaker 274 breaker.out = out 275 276 b64 := base64.NewEncoder(base64.StdEncoding, &breaker) 277 if _, err := b64.Write(b.Bytes); err != nil { 278 return err 279 } 280 b64.Close() 281 breaker.Close() 282 283 if _, err := out.Write(pemEnd[1:]); err != nil { 284 return err 285 } 286 _, err := out.Write([]byte(b.Type + "-----\n")) 287 return err 288 } 289 290 func EncodeToMemory(b *Block) []byte { 291 var buf bytes.Buffer 292 Encode(&buf, b) 293 return buf.Bytes() 294 } 295