github.com/ccccaoqing/test@v0.0.0-20220510085219-3985d23445c0/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 func (l *lineBreaker) Write(b []byte) (n int, err error) { 175 if l.used+len(b) < pemLineLength { 176 copy(l.line[l.used:], b) 177 l.used += len(b) 178 return len(b), nil 179 } 180 181 n, err = l.out.Write(l.line[0:l.used]) 182 if err != nil { 183 return 184 } 185 excess := pemLineLength - l.used 186 l.used = 0 187 188 n, err = l.out.Write(b[0:excess]) 189 if err != nil { 190 return 191 } 192 193 n, err = l.out.Write([]byte{'\n'}) 194 if err != nil { 195 return 196 } 197 198 return l.Write(b[excess:]) 199 } 200 201 func (l *lineBreaker) Close() (err error) { 202 if l.used > 0 { 203 _, err = l.out.Write(l.line[0:l.used]) 204 if err != nil { 205 return 206 } 207 _, err = l.out.Write([]byte{'\n'}) 208 } 209 210 return 211 } 212 213 func writeHeader(out io.Writer, k, v string) error { 214 _, err := out.Write([]byte(k + ": " + v + "\n")) 215 return err 216 } 217 218 func Encode(out io.Writer, b *Block) error { 219 if _, err := out.Write(pemStart[1:]); err != nil { 220 return err 221 } 222 if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { 223 return err 224 } 225 226 if len(b.Headers) > 0 { 227 const procType = "Proc-Type" 228 h := make([]string, 0, len(b.Headers)) 229 hasProcType := false 230 for k := range b.Headers { 231 if k == procType { 232 hasProcType = true 233 continue 234 } 235 h = append(h, k) 236 } 237 // The Proc-Type header must be written first. 238 // See RFC 1421, section 4.6.1.1 239 if hasProcType { 240 if err := writeHeader(out, procType, b.Headers[procType]); err != nil { 241 return err 242 } 243 } 244 // For consistency of output, write other headers sorted by key. 245 sort.Strings(h) 246 for _, k := range h { 247 if err := writeHeader(out, k, b.Headers[k]); err != nil { 248 return err 249 } 250 } 251 if _, err := out.Write([]byte{'\n'}); err != nil { 252 return err 253 } 254 } 255 256 var breaker lineBreaker 257 breaker.out = out 258 259 b64 := base64.NewEncoder(base64.StdEncoding, &breaker) 260 if _, err := b64.Write(b.Bytes); err != nil { 261 return err 262 } 263 b64.Close() 264 breaker.Close() 265 266 if _, err := out.Write(pemEnd[1:]); err != nil { 267 return err 268 } 269 _, err := out.Write([]byte(b.Type + "-----\n")) 270 return err 271 } 272 273 func EncodeToMemory(b *Block) []byte { 274 var buf bytes.Buffer 275 Encode(&buf, b) 276 return buf.Bytes() 277 }