github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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.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, 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 should be the same type and a 139 // 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 endTrailer = endTrailer[:endTrailerLen] 147 if !bytes.HasPrefix(endTrailer, typeLine) || 148 !bytes.HasSuffix(endTrailer, pemEndOfLine) { 149 return decodeError(data, rest) 150 } 151 152 base64Data := removeWhitespace(rest[:endIndex]) 153 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) 154 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) 155 if err != nil { 156 return decodeError(data, rest) 157 } 158 p.Bytes = p.Bytes[:n] 159 160 // the -1 is because we might have only matched pemEnd without the 161 // leading newline if the PEM block was empty. 162 _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) 163 164 return 165 } 166 167 func decodeError(data, rest []byte) (*Block, []byte) { 168 // If we get here then we have rejected a likely looking, but 169 // ultimately invalid PEM block. We need to start over from a new 170 // position. We have consumed the preamble line and will have consumed 171 // any lines which could be header lines. However, a valid preamble 172 // line is not a valid header line, therefore we cannot have consumed 173 // the preamble line for the any subsequent block. Thus, we will always 174 // find any valid block, no matter what bytes precede it. 175 // 176 // For example, if the input is 177 // 178 // -----BEGIN MALFORMED BLOCK----- 179 // junk that may look like header lines 180 // or data lines, but no END line 181 // 182 // -----BEGIN ACTUAL BLOCK----- 183 // realdata 184 // -----END ACTUAL BLOCK----- 185 // 186 // we've failed to parse using the first BEGIN line 187 // and now will try again, using the second BEGIN line. 188 p, rest := Decode(rest) 189 if p == nil { 190 rest = data 191 } 192 return p, rest 193 } 194 195 const pemLineLength = 64 196 197 type lineBreaker struct { 198 line [pemLineLength]byte 199 used int 200 out io.Writer 201 } 202 203 var nl = []byte{'\n'} 204 205 func (l *lineBreaker) Write(b []byte) (n int, err error) { 206 if l.used+len(b) < pemLineLength { 207 copy(l.line[l.used:], b) 208 l.used += len(b) 209 return len(b), nil 210 } 211 212 n, err = l.out.Write(l.line[0:l.used]) 213 if err != nil { 214 return 215 } 216 excess := pemLineLength - l.used 217 l.used = 0 218 219 n, err = l.out.Write(b[0:excess]) 220 if err != nil { 221 return 222 } 223 224 n, err = l.out.Write(nl) 225 if err != nil { 226 return 227 } 228 229 return l.Write(b[excess:]) 230 } 231 232 func (l *lineBreaker) Close() (err error) { 233 if l.used > 0 { 234 _, err = l.out.Write(l.line[0:l.used]) 235 if err != nil { 236 return 237 } 238 _, err = l.out.Write(nl) 239 } 240 241 return 242 } 243 244 func writeHeader(out io.Writer, k, v string) error { 245 _, err := out.Write([]byte(k + ": " + v + "\n")) 246 return err 247 } 248 249 func Encode(out io.Writer, b *Block) error { 250 if _, err := out.Write(pemStart[1:]); err != nil { 251 return err 252 } 253 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { 254 return err 255 } 256 257 if len(b.Headers) > 0 { 258 const procType = "Proc-Type" 259 h := make([]string, 0, len(b.Headers)) 260 hasProcType := false 261 for k := range b.Headers { 262 if k == procType { 263 hasProcType = true 264 continue 265 } 266 h = append(h, k) 267 } 268 // The Proc-Type header must be written first. 269 // See RFC 1421, section 4.6.1.1 270 if hasProcType { 271 if err := writeHeader(out, procType, b.Headers[procType]); err != nil { 272 return err 273 } 274 } 275 // For consistency of output, write other headers sorted by key. 276 sort.Strings(h) 277 for _, k := range h { 278 if strings.Contains(k, ":") { 279 return errors.New("pem: cannot encode a header key that contains a colon") 280 } 281 if err := writeHeader(out, k, b.Headers[k]); err != nil { 282 return err 283 } 284 } 285 if _, err := out.Write(nl); err != nil { 286 return err 287 } 288 } 289 290 var breaker lineBreaker 291 breaker.out = out 292 293 b64 := base64.NewEncoder(base64.StdEncoding, &breaker) 294 if _, err := b64.Write(b.Bytes); err != nil { 295 return err 296 } 297 b64.Close() 298 breaker.Close() 299 300 if _, err := out.Write(pemEnd[1:]); err != nil { 301 return err 302 } 303 _, err := out.Write([]byte(b.Type + "-----\n")) 304 return err 305 } 306 307 func EncodeToMemory(b *Block) []byte { 308 var buf bytes.Buffer 309 Encode(&buf, b) 310 return buf.Bytes() 311 }