github.com/hongwozai/go-src-1.4.3@v0.0.0-20191127132709-dc3fce3dbccb/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  }