github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/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 "io" 14 "sort" 15 ) 16 17 // A Block represents a PEM encoded structure. 18 // 19 // The encoded form is: 20 // -----BEGIN Type----- 21 // Headers 22 // base64-encoded Bytes 23 // -----END Type----- 24 // where Headers is a possibly empty sequence of Key: Value lines. 25 type Block struct { 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. 29 } 30 31 // getLine results the first \r\n or \n delineated line from the given byte 32 // array. The line does not include trailing whitespace or the trailing new 33 // line bytes. The remainder of the byte array (also not including the new line 34 // bytes) is also returned and this will always be smaller than the original 35 // argument. 36 func getLine(data []byte) (line, rest []byte) { 37 i := bytes.Index(data, []byte{'\n'}) 38 var j int 39 if i < 0 { 40 i = len(data) 41 j = i 42 } else { 43 j = i + 1 44 if i > 0 && data[i-1] == '\r' { 45 i-- 46 } 47 } 48 return bytes.TrimRight(data[0:i], " \t"), data[j:] 49 } 50 51 // removeWhitespace returns a copy of its input with all spaces, tab and 52 // newline characters removed. 53 func removeWhitespace(data []byte) []byte { 54 result := make([]byte, len(data)) 55 n := 0 56 57 for _, b := range data { 58 if b == ' ' || b == '\t' || b == '\r' || b == '\n' { 59 continue 60 } 61 result[n] = b 62 n++ 63 } 64 65 return result[0:n] 66 } 67 68 var pemStart = []byte("\n-----BEGIN ") 69 var pemEnd = []byte("\n-----END ") 70 var pemEndOfLine = []byte("-----") 71 72 // Decode will find the next PEM formatted block (certificate, private key 73 // etc) in the input. It returns that block and the remainder of the input. If 74 // no PEM data is found, p is nil and the whole of the input is returned in 75 // rest. 76 func Decode(data []byte) (p *Block, rest []byte) { 77 // pemStart begins with a newline. However, at the very beginning of 78 // the byte array, we'll accept the start string without it. 79 rest = data 80 if bytes.HasPrefix(data, pemStart[1:]) { 81 rest = rest[len(pemStart)-1 : len(data)] 82 } else if i := bytes.Index(data, pemStart); i >= 0 { 83 rest = rest[i+len(pemStart) : len(data)] 84 } else { 85 return nil, data 86 } 87 88 typeLine, rest := getLine(rest) 89 if !bytes.HasSuffix(typeLine, pemEndOfLine) { 90 return decodeError(data, rest) 91 } 92 typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] 93 94 p = &Block{ 95 Headers: make(map[string]string), 96 Type: string(typeLine), 97 } 98 99 for { 100 // This loop terminates because getLine's second result is 101 // always smaller than its argument. 102 if len(rest) == 0 { 103 return nil, data 104 } 105 line, next := getLine(rest) 106 107 i := bytes.Index(line, []byte{':'}) 108 if i == -1 { 109 break 110 } 111 112 // TODO(agl): need to cope with values that spread across lines. 113 key, val := line[0:i], line[i+1:] 114 key = bytes.TrimSpace(key) 115 val = bytes.TrimSpace(val) 116 p.Headers[string(key)] = string(val) 117 rest = next 118 } 119 120 i := bytes.Index(rest, pemEnd) 121 if i < 0 { 122 return decodeError(data, rest) 123 } 124 base64Data := removeWhitespace(rest[0:i]) 125 126 p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) 127 n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) 128 if err != nil { 129 return decodeError(data, rest) 130 } 131 p.Bytes = p.Bytes[0:n] 132 133 _, rest = getLine(rest[i+len(pemEnd):]) 134 135 return 136 } 137 138 func decodeError(data, rest []byte) (*Block, []byte) { 139 // If we get here then we have rejected a likely looking, but 140 // ultimately invalid PEM block. We need to start over from a new 141 // position. We have consumed the preamble line and will have consumed 142 // any lines which could be header lines. However, a valid preamble 143 // line is not a valid header line, therefore we cannot have consumed 144 // the preamble line for the any subsequent block. Thus, we will always 145 // find any valid block, no matter what bytes precede it. 146 // 147 // For example, if the input is 148 // 149 // -----BEGIN MALFORMED BLOCK----- 150 // junk that may look like header lines 151 // or data lines, but no END line 152 // 153 // -----BEGIN ACTUAL BLOCK----- 154 // realdata 155 // -----END ACTUAL BLOCK----- 156 // 157 // we've failed to parse using the first BEGIN line 158 // and now will try again, using the second BEGIN line. 159 p, rest := Decode(rest) 160 if p == nil { 161 rest = data 162 } 163 return p, rest 164 } 165 166 const pemLineLength = 64 167 168 type lineBreaker struct { 169 line [pemLineLength]byte 170 used int 171 out io.Writer 172 } 173 174 var nl = []byte{'\n'} 175 176 func (l *lineBreaker) Write(b []byte) (n int, err error) { 177 if l.used+len(b) < pemLineLength { 178 copy(l.line[l.used:], b) 179 l.used += len(b) 180 return len(b), nil 181 } 182 183 n, err = l.out.Write(l.line[0:l.used]) 184 if err != nil { 185 return 186 } 187 excess := pemLineLength - l.used 188 l.used = 0 189 190 n, err = l.out.Write(b[0:excess]) 191 if err != nil { 192 return 193 } 194 195 n, err = l.out.Write(nl) 196 if err != nil { 197 return 198 } 199 200 return l.Write(b[excess:]) 201 } 202 203 func (l *lineBreaker) Close() (err error) { 204 if l.used > 0 { 205 _, err = l.out.Write(l.line[0:l.used]) 206 if err != nil { 207 return 208 } 209 _, err = l.out.Write(nl) 210 } 211 212 return 213 } 214 215 func writeHeader(out io.Writer, k, v string) error { 216 _, err := out.Write([]byte(k + ": " + v + "\n")) 217 return err 218 } 219 220 func Encode(out io.Writer, b *Block) error { 221 if _, err := out.Write(pemStart[1:]); err != nil { 222 return err 223 } 224 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { 225 return err 226 } 227 228 if len(b.Headers) > 0 { 229 const procType = "Proc-Type" 230 h := make([]string, 0, len(b.Headers)) 231 hasProcType := false 232 for k := range b.Headers { 233 if k == procType { 234 hasProcType = true 235 continue 236 } 237 h = append(h, k) 238 } 239 // The Proc-Type header must be written first. 240 // See RFC 1421, section 4.6.1.1 241 if hasProcType { 242 if err := writeHeader(out, procType, b.Headers[procType]); err != nil { 243 return err 244 } 245 } 246 // For consistency of output, write other headers sorted by key. 247 sort.Strings(h) 248 for _, k := range h { 249 if err := writeHeader(out, k, b.Headers[k]); err != nil { 250 return err 251 } 252 } 253 if _, err := out.Write(nl); err != nil { 254 return err 255 } 256 } 257 258 var breaker lineBreaker 259 breaker.out = out 260 261 b64 := base64.NewEncoder(base64.StdEncoding, &breaker) 262 if _, err := b64.Write(b.Bytes); err != nil { 263 return err 264 } 265 b64.Close() 266 breaker.Close() 267 268 if _, err := out.Write(pemEnd[1:]); err != nil { 269 return err 270 } 271 _, err := out.Write([]byte(b.Type + "-----\n")) 272 return err 273 } 274 275 func EncodeToMemory(b *Block) []byte { 276 var buf bytes.Buffer 277 Encode(&buf, b) 278 return buf.Bytes() 279 }