github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/encoding/pem/pem.go (about) 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.IndexByte(data, '\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.IndexByte(line, ':') 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, endTrailerIndex int 123 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:]) { 127 endIndex = 0 128 endTrailerIndex = len(pemEnd) - 1 129 } else { 130 endIndex = bytes.Index(rest, pemEnd) 131 endTrailerIndex = endIndex + len(pemEnd) 132 } 133 134 if endIndex < 0 { 135 return decodeError(data, rest) 136 } 137 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) 144 } 145 146 restOfEndLine := endTrailer[endTrailerLen:] 147 endTrailer = endTrailer[:endTrailerLen] 148 if !bytes.HasPrefix(endTrailer, typeLine) || 149 !bytes.HasSuffix(endTrailer, pemEndOfLine) { 150 return decodeError(data, rest) 151 } 152 153 // The line must end with only whitespace. 154 if s, _ := getLine(restOfEndLine); len(s) != 0 { 155 return decodeError(data, rest) 156 } 157 158 base64Data := removeWhitespace(rest[:endIndex]) 159 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) 160 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) 161 if err != nil { 162 return decodeError(data, rest) 163 } 164 p.Bytes = p.Bytes[:n] 165 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:]) 169 170 return 171 } 172 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. 181 // 182 // For example, if the input is 183 // 184 // -----BEGIN MALFORMED BLOCK----- 185 // junk that may look like header lines 186 // or data lines, but no END line 187 // 188 // -----BEGIN ACTUAL BLOCK----- 189 // realdata 190 // -----END ACTUAL BLOCK----- 191 // 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) 195 if p == nil { 196 rest = data 197 } 198 return p, rest 199 } 200 201 const pemLineLength = 64 202 203 type lineBreaker struct { 204 line [pemLineLength]byte 205 used int 206 out io.Writer 207 } 208 209 var nl = []byte{'\n'} 210 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) 214 l.used += len(b) 215 return len(b), nil 216 } 217 218 n, err = l.out.Write(l.line[0:l.used]) 219 if err != nil { 220 return 221 } 222 excess := pemLineLength - l.used 223 l.used = 0 224 225 n, err = l.out.Write(b[0:excess]) 226 if err != nil { 227 return 228 } 229 230 n, err = l.out.Write(nl) 231 if err != nil { 232 return 233 } 234 235 return l.Write(b[excess:]) 236 } 237 238 func (l *lineBreaker) Close() (err error) { 239 if l.used > 0 { 240 _, err = l.out.Write(l.line[0:l.used]) 241 if err != nil { 242 return 243 } 244 _, err = l.out.Write(nl) 245 } 246 247 return 248 } 249 250 func writeHeader(out io.Writer, k, v string) error { 251 _, err := out.Write([]byte(k + ": " + v + "\n")) 252 return err 253 } 254 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") 261 } 262 } 263 264 // All errors below are relayed from underlying io.Writer, 265 // so it is now safe to write data. 266 267 if _, err := out.Write(pemStart[1:]); err != nil { 268 return err 269 } 270 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { 271 return err 272 } 273 274 if len(b.Headers) > 0 { 275 const procType = "Proc-Type" 276 h := make([]string, 0, len(b.Headers)) 277 hasProcType := false 278 for k := range b.Headers { 279 if k == procType { 280 hasProcType = true 281 continue 282 } 283 h = append(h, k) 284 } 285 // The Proc-Type header must be written first. 286 // See RFC 1421, section 4.6.1.1 287 if hasProcType { 288 if err := writeHeader(out, procType, b.Headers[procType]); err != nil { 289 return err 290 } 291 } 292 // For consistency of output, write other headers sorted by key. 293 sort.Strings(h) 294 for _, k := range h { 295 if err := writeHeader(out, k, b.Headers[k]); err != nil { 296 return err 297 } 298 } 299 if _, err := out.Write(nl); err != nil { 300 return err 301 } 302 } 303 304 var breaker lineBreaker 305 breaker.out = out 306 307 b64 := base64.NewEncoder(base64.StdEncoding, &breaker) 308 if _, err := b64.Write(b.Bytes); err != nil { 309 return err 310 } 311 b64.Close() 312 breaker.Close() 313 314 if _, err := out.Write(pemEnd[1:]); err != nil { 315 return err 316 } 317 _, err := out.Write([]byte(b.Type + "-----\n")) 318 return err 319 } 320 321 // EncodeToMemory returns the PEM encoding of b. 322 // 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 { 327 var buf bytes.Buffer 328 if err := Encode(&buf, b); err != nil { 329 return nil 330 } 331 return buf.Bytes() 332 }