Skip to content

Commit 03fcfce

Browse files
dopeymaraino
andauthored
Allow extra content in Certificate and CSR PEM files (#488)
* Introduce new InvalidPEMError that can be validated by clients Co-authored-by: Mariano Cano <[email protected]> --------- Co-authored-by: Mariano Cano <[email protected]>
1 parent 1d8dca8 commit 03fcfce

File tree

2 files changed

+78
-13
lines changed

2 files changed

+78
-13
lines changed

pemutil/pem.go

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"fmt"
1717
"math/big"
1818
"os"
19+
"strings"
1920

2021
"github.com/pkg/errors"
2122
"go.step.sm/crypto/internal/utils"
@@ -277,6 +278,60 @@ func ParseCertificateRequest(pemData []byte) (*x509.CertificateRequest, error) {
277278
return nil, errors.New("error parsing certificate request: no certificate found")
278279
}
279280

281+
// PEMType represents a PEM block type. (e.g., CERTIFICATE, CERTIFICATE REQUEST, etc.)
282+
type PEMType int
283+
284+
func (pt PEMType) String() string {
285+
switch pt {
286+
case PEMTypeCertificate:
287+
return "certificate"
288+
case PEMTypeCertificateRequest:
289+
return "certificate request"
290+
default:
291+
return "undefined"
292+
}
293+
}
294+
295+
const (
296+
// PEMTypeUndefined undefined
297+
PEMTypeUndefined = iota
298+
// PEMTypeCertificate CERTIFICATE
299+
PEMTypeCertificate
300+
// PEMTypeCertificateRequest CERTIFICATE REQUEST
301+
PEMTypeCertificateRequest
302+
)
303+
304+
// InvalidPEMError represents an error that occurs when parsing a file with
305+
// PEM encoded data.
306+
type InvalidPEMError struct {
307+
Type PEMType
308+
File string
309+
Message string
310+
Err error
311+
}
312+
313+
func (e *InvalidPEMError) Error() string {
314+
switch {
315+
case e.Message != "":
316+
return e.Message
317+
case e.Err != nil:
318+
return fmt.Sprintf("error decoding PEM data: %v", e.Err)
319+
default:
320+
var prefix = "input"
321+
if e.File != "" {
322+
prefix = fmt.Sprintf("file %s", e.File)
323+
}
324+
if e.Type == PEMTypeUndefined {
325+
return fmt.Sprintf("%s does not contain valid PEM encoded data", prefix)
326+
}
327+
return fmt.Sprintf("%s does not contain a valid PEM encoded %s", prefix, e.Type)
328+
}
329+
}
330+
331+
func (e *InvalidPEMError) Unwrap() error {
332+
return e.Err
333+
}
334+
280335
// ReadCertificate returns a *x509.Certificate from the given filename. It
281336
// supports certificates formats PEM and DER.
282337
func ReadCertificate(filename string, opts ...Options) (*x509.Certificate, error) {
@@ -328,7 +383,7 @@ func ReadCertificateBundle(filename string) ([]*x509.Certificate, error) {
328383
bundle = append(bundle, crt)
329384
}
330385
if len(bundle) == 0 {
331-
return nil, errors.Errorf("file %s does not contain a valid PEM formatted certificate", filename)
386+
return nil, &InvalidPEMError{File: filename, Type: PEMTypeCertificate}
332387
}
333388
return bundle, nil
334389
}
@@ -351,15 +406,25 @@ func ReadCertificateRequest(filename string) (*x509.CertificateRequest, error) {
351406

352407
// PEM format
353408
if bytes.Contains(b, PEMBlockHeader) {
354-
csr, err := Parse(b, WithFilename(filename))
355-
if err != nil {
356-
return nil, err
357-
}
358-
switch csr := csr.(type) {
359-
case *x509.CertificateRequest:
409+
var block *pem.Block
410+
for len(b) > 0 {
411+
block, b = pem.Decode(b)
412+
if block == nil {
413+
break
414+
}
415+
if !strings.HasSuffix(block.Type, "CERTIFICATE REQUEST") {
416+
continue
417+
}
418+
csr, err := x509.ParseCertificateRequest(block.Bytes)
419+
if err != nil {
420+
return nil, &InvalidPEMError{
421+
File: filename, Type: PEMTypeCertificateRequest,
422+
Message: fmt.Sprintf("error parsing %s: CSR PEM block is invalid: %v", filename, err),
423+
Err: err,
424+
}
425+
}
426+
360427
return csr, nil
361-
default:
362-
return nil, errors.Errorf("error decoding PEM: file '%s' does not contain a certificate request", filename)
363428
}
364429
}
365430

pemutil/pem_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,9 @@ func TestReadCertificate(t *testing.T) {
345345
{"testdata/bundle.crt", nil, errors.New("error decoding testdata/bundle.crt: contains more than one PEM encoded block")},
346346
{"testdata/notexists.crt", nil, errors.New("error reading testdata/notexists.crt: no such file or directory")},
347347
{"testdata/badca.crt", nil, errors.New("error parsing testdata/badca.crt")},
348-
{"testdata/badpem.crt", nil, errors.New("file testdata/badpem.crt does not contain a valid PEM formatted certificate")},
348+
{"testdata/badpem.crt", nil, errors.New("file testdata/badpem.crt does not contain a valid PEM encoded certificate")},
349349
{"testdata/badder.crt", nil, errors.New("error parsing testdata/badder.crt")},
350-
{"testdata/openssl.p256.pem", nil, errors.New("file testdata/openssl.p256.pem does not contain a valid PEM formatted certificate")},
350+
{"testdata/openssl.p256.pem", nil, errors.New("file testdata/openssl.p256.pem does not contain a valid PEM encoded certificate")},
351351
}
352352

353353
for _, tc := range tests {
@@ -378,9 +378,9 @@ func TestReadCertificateBundle(t *testing.T) {
378378
{"testdata/extrajunkbundle.crt", 2, nil},
379379
{"testdata/notexists.crt", 0, errors.New("error reading testdata/notexists.crt: no such file or directory")},
380380
{"testdata/badca.crt", 0, errors.New("error parsing testdata/badca.crt")},
381-
{"testdata/badpem.crt", 0, errors.New("file testdata/badpem.crt does not contain a valid PEM formatted certificate")},
381+
{"testdata/badpem.crt", 0, errors.New("file testdata/badpem.crt does not contain a valid PEM encoded certificate")},
382382
{"testdata/badder.crt", 0, errors.New("error parsing testdata/badder.crt")},
383-
{"testdata/openssl.p256.pem", 0, errors.New("file testdata/openssl.p256.pem does not contain a valid PEM formatted certificate")},
383+
{"testdata/openssl.p256.pem", 0, errors.New("file testdata/openssl.p256.pem does not contain a valid PEM encoded certificate")},
384384
}
385385

386386
for _, tc := range tests {

0 commit comments

Comments
 (0)