// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.
// Package pem implements the PEM data encoding, which originated in Privacy// Enhanced Mail. The most common use of PEM encoding today is in TLS keys and// certificates. See RFC 1421.
package pemimport ()// A Block represents a PEM encoded structure.//// The encoded form is://// -----BEGIN Type-----// Headers// base64-encoded Bytes// -----END Type-----//// where [Block.Headers] is a possibly empty sequence of Key: Value lines.typeBlockstruct { Type string// The type, taken from the preamble (i.e. "RSA PRIVATE KEY"). Headers map[string]string// Optional headers. Bytes []byte// The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.}// getLine results the first \r\n or \n delineated line from the given byte// array. The line does not include trailing whitespace or the trailing new// line bytes. The remainder of the byte array (also not including the new line// bytes) is also returned and this will always be smaller than the original// argument.func getLine( []byte) (, []byte) { := bytes.IndexByte(, '\n')varintif < 0 { = len() = } else { = + 1if > 0 && [-1] == '\r' { -- } }returnbytes.TrimRight([0:], " \t"), [:]}// removeSpacesAndTabs returns a copy of its input with all spaces and tabs// removed, if there were any. Otherwise, the input is returned unchanged.//// The base64 decoder already skips newline characters, so we don't need to// filter them out here.func removeSpacesAndTabs( []byte) []byte {if !bytes.ContainsAny(, " \t") {// Fast path; most base64 data within PEM contains newlines, but // no spaces nor tabs. Skip the extra alloc and work.return } := make([]byte, len()) := 0for , := range {if == ' ' || == '\t' {continue } [] = ++ }return [0:]}var pemStart = []byte("\n-----BEGIN ")var pemEnd = []byte("\n-----END ")var pemEndOfLine = []byte("-----")var colon = []byte(":")// Decode will find the next PEM formatted block (certificate, private key// etc) in the input. It returns that block and the remainder of the input. If// no PEM data is found, p is nil and the whole of the input is returned in// rest.func ( []byte) ( *Block, []byte) {// pemStart begins with a newline. However, at the very beginning of // the byte array, we'll accept the start string without it. = for {ifbytes.HasPrefix(, pemStart[1:]) { = [len(pemStart)-1:] } elseif , , := bytes.Cut(, pemStart); { = } else {returnnil, }var []byte , = getLine()if !bytes.HasSuffix(, pemEndOfLine) {continue } = [0 : len()-len(pemEndOfLine)] = &Block{Headers: make(map[string]string),Type: string(), }for {// This loop terminates because getLine's second result is // always smaller than its argument.iflen() == 0 {returnnil, } , := getLine() , , := bytes.Cut(, colon)if ! {break }// TODO(agl): need to cope with values that spread across lines. = bytes.TrimSpace() = bytes.TrimSpace() .Headers[string()] = string() = }var , int// If there were no headers, the END line might occur // immediately, without a leading newline.iflen(.Headers) == 0 && bytes.HasPrefix(, pemEnd[1:]) { = 0 = len(pemEnd) - 1 } else { = bytes.Index(, pemEnd) = + len(pemEnd) }if < 0 {continue }// After the "-----" of the ending line, there should be the same type // and then a final five dashes. := [:] := len() + len(pemEndOfLine)iflen() < {continue } := [:] = [:]if !bytes.HasPrefix(, ) || !bytes.HasSuffix(, pemEndOfLine) {continue }// The line must end with only whitespace.if , := getLine(); len() != 0 {continue } := removeSpacesAndTabs([:]) .Bytes = make([]byte, base64.StdEncoding.DecodedLen(len())) , := base64.StdEncoding.Decode(.Bytes, )if != nil {continue } .Bytes = .Bytes[:]// the -1 is because we might have only matched pemEnd without the // leading newline if the PEM block was empty. _, = getLine([+len(pemEnd)-1:])return , }}const pemLineLength = 64type lineBreaker struct { line [pemLineLength]byte used int out io.Writer}var nl = []byte{'\n'}func ( *lineBreaker) ( []byte) ( int, error) {if .used+len() < pemLineLength {copy(.line[.used:], ) .used += len()returnlen(), nil } , = .out.Write(.line[0:.used])if != nil {return } := pemLineLength - .used .used = 0 , = .out.Write([0:])if != nil {return } , = .out.Write(nl)if != nil {return }return .([:])}func ( *lineBreaker) () ( error) {if .used > 0 { _, = .out.Write(.line[0:.used])if != nil {return } _, = .out.Write(nl) }return}func writeHeader( io.Writer, , string) error { , := .Write([]byte( + ": " + + "\n"))return}// Encode writes the PEM encoding of b to out.func ( io.Writer, *Block) error {// Check for invalid block before writing any output.for := range .Headers {ifstrings.Contains(, ":") {returnerrors.New("pem: cannot encode a header key that contains a colon") } }// All errors below are relayed from underlying io.Writer, // so it is now safe to write data.if , := .Write(pemStart[1:]); != nil {return }if , := .Write([]byte(.Type + "-----\n")); != nil {return }iflen(.Headers) > 0 {const = "Proc-Type" := make([]string, 0, len(.Headers)) := falsefor := range .Headers {if == { = truecontinue } = append(, ) }// The Proc-Type header must be written first. // See RFC 1421, section 4.6.1.1if {if := writeHeader(, , .Headers[]); != nil {return } }// For consistency of output, write other headers sorted by key.slices.Sort()for , := range {if := writeHeader(, , .Headers[]); != nil {return } }if , := .Write(nl); != nil {return } }varlineBreaker .out = := base64.NewEncoder(base64.StdEncoding, &)if , := .Write(.Bytes); != nil {return } .Close() .Close()if , := .Write(pemEnd[1:]); != nil {return } , := .Write([]byte(.Type + "-----\n"))return}// EncodeToMemory returns the PEM encoding of b.//// If b has invalid headers and cannot be encoded,// EncodeToMemory returns nil. If it is important to// report details about this error case, use [Encode] instead.func ( *Block) []byte {varbytes.Bufferif := Encode(&, ); != nil {returnnil }return .Bytes()}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.