| // Copyright Istio Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package util |
| |
| import ( |
| "crypto" |
| "crypto/ecdsa" |
| "crypto/rsa" |
| "crypto/x509" |
| "encoding/pem" |
| "fmt" |
| "reflect" |
| "strings" |
| ) |
| |
| const ( |
| blockTypeECPrivateKey = "EC PRIVATE KEY" |
| blockTypeRSAPrivateKey = "RSA PRIVATE KEY" // PKCS#1 private key |
| blockTypePKCS8PrivateKey = "PRIVATE KEY" // PKCS#8 plain private key |
| ) |
| |
| // ParsePemEncodedCertificate constructs a `x509.Certificate` object using the |
| // given a PEM-encoded certificate. |
| func ParsePemEncodedCertificate(certBytes []byte) (*x509.Certificate, error) { |
| cb, _ := pem.Decode(certBytes) |
| if cb == nil { |
| return nil, fmt.Errorf("invalid PEM encoded certificate") |
| } |
| |
| cert, err := x509.ParseCertificate(cb.Bytes) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse X.509 certificate") |
| } |
| |
| return cert, nil |
| } |
| |
| // ParsePemEncodedCertificateChain constructs a slice of `x509.Certificate` |
| // objects using the given a PEM-encoded certificate chain. |
| func ParsePemEncodedCertificateChain(certBytes []byte) ([]*x509.Certificate, error) { |
| var ( |
| certs []*x509.Certificate |
| cb *pem.Block |
| ) |
| for { |
| cb, certBytes = pem.Decode(certBytes) |
| if cb == nil { |
| break |
| } |
| cert, err := x509.ParseCertificate(cb.Bytes) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse X.509 certificate") |
| } |
| certs = append(certs, cert) |
| } |
| if len(certs) == 0 { |
| return nil, fmt.Errorf("no PEM encoded X.509 certificates parsed") |
| } |
| return certs, nil |
| } |
| |
| // ParsePemEncodedCSR constructs a `x509.CertificateRequest` object using the |
| // given PEM-encoded certificate signing request. |
| func ParsePemEncodedCSR(csrBytes []byte) (*x509.CertificateRequest, error) { |
| block, _ := pem.Decode(csrBytes) |
| if block == nil { |
| return nil, fmt.Errorf("certificate signing request is not properly encoded") |
| } |
| csr, err := x509.ParseCertificateRequest(block.Bytes) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse X.509 certificate signing request") |
| } |
| return csr, nil |
| } |
| |
| // ParsePemEncodedKey takes a PEM-encoded key and parsed the bytes into a `crypto.PrivateKey`. |
| func ParsePemEncodedKey(keyBytes []byte) (crypto.PrivateKey, error) { |
| kb, _ := pem.Decode(keyBytes) |
| if kb == nil { |
| return nil, fmt.Errorf("invalid PEM-encoded key") |
| } |
| |
| switch kb.Type { |
| case blockTypeECPrivateKey: |
| key, err := x509.ParseECPrivateKey(kb.Bytes) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse the ECDSA private key") |
| } |
| return key, nil |
| case blockTypeRSAPrivateKey: |
| key, err := x509.ParsePKCS1PrivateKey(kb.Bytes) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse the RSA private key") |
| } |
| return key, nil |
| case blockTypePKCS8PrivateKey: |
| key, err := x509.ParsePKCS8PrivateKey(kb.Bytes) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse the PKCS8 private key") |
| } |
| return key, nil |
| default: |
| return nil, fmt.Errorf("unsupported PEM block type for a private key: %s", kb.Type) |
| } |
| } |
| |
| // GetRSAKeySize returns the size if it is RSA key, otherwise it returns an error. |
| func GetRSAKeySize(privKey crypto.PrivateKey) (int, error) { |
| if t := reflect.TypeOf(privKey); t != reflect.TypeOf(&rsa.PrivateKey{}) { |
| return 0, fmt.Errorf("key type is not RSA: %v", t) |
| } |
| pkey := privKey.(*rsa.PrivateKey) |
| return pkey.N.BitLen(), nil |
| } |
| |
| // IsSupportedECPrivateKey is a predicate returning true if the private key is EC based |
| func IsSupportedECPrivateKey(privKey *crypto.PrivateKey) bool { |
| switch (*privKey).(type) { |
| // this should agree with var SupportedECSignatureAlgorithms |
| case *ecdsa.PrivateKey: |
| return true |
| default: |
| return false |
| } |
| } |
| |
| // PemCertBytestoString: takes an array of PEM certs in bytes and returns a string array in the same order with |
| // trailing newline characters removed |
| func PemCertBytestoString(caCerts []byte) []string { |
| certs := []string{} |
| var cert string |
| pemBlock := caCerts |
| for block, rest := pem.Decode(pemBlock); block != nil && len(block.Bytes) != 0; block, rest = pem.Decode(pemBlock) { |
| if len(rest) == 0 { |
| cert = strings.TrimPrefix(strings.TrimSuffix(string(pemBlock), "\n"), "\n") |
| certs = append(certs, cert) |
| break |
| } |
| cert = string(pemBlock[0 : len(pemBlock)-len(rest)]) |
| cert = strings.TrimPrefix(strings.TrimSuffix(cert, "\n"), "\n") |
| certs = append(certs, cert) |
| pemBlock = rest |
| } |
| return certs |
| } |