istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/pki/util/crypto.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "crypto" 19 "crypto/ecdsa" 20 "crypto/elliptic" 21 "crypto/rsa" 22 "crypto/x509" 23 "encoding/pem" 24 "fmt" 25 "reflect" 26 "strings" 27 ) 28 29 const ( 30 blockTypeECPrivateKey = "EC PRIVATE KEY" 31 blockTypeRSAPrivateKey = "RSA PRIVATE KEY" // PKCS#1 private key 32 blockTypePKCS8PrivateKey = "PRIVATE KEY" // PKCS#8 plain private key 33 ) 34 35 // ParsePemEncodedCertificate constructs a `x509.Certificate` object using the 36 // given a PEM-encoded certificate. 37 func ParsePemEncodedCertificate(certBytes []byte) (*x509.Certificate, error) { 38 cb, _ := pem.Decode(certBytes) 39 if cb == nil { 40 return nil, fmt.Errorf("invalid PEM encoded certificate") 41 } 42 43 cert, err := x509.ParseCertificate(cb.Bytes) 44 if err != nil { 45 return nil, fmt.Errorf("failed to parse X.509 certificate") 46 } 47 48 return cert, nil 49 } 50 51 // ParsePemEncodedCertificateChain constructs a slice of `x509.Certificate` and `rootCertBytes` 52 // objects using the given a PEM-encoded certificate chain. 53 func ParsePemEncodedCertificateChain(certBytes []byte) ([]*x509.Certificate, []byte, error) { 54 var ( 55 certs []*x509.Certificate 56 cb *pem.Block 57 rootCertBytes []byte 58 ) 59 for { 60 rootCertBytes = certBytes 61 cb, certBytes = pem.Decode(certBytes) 62 if cb == nil { 63 return nil, nil, fmt.Errorf("invalid PEM encoded certificate") 64 } 65 cert, err := x509.ParseCertificate(cb.Bytes) 66 if err != nil { 67 return nil, nil, fmt.Errorf("failed to parse X.509 certificate") 68 } 69 certs = append(certs, cert) 70 if len(certBytes) == 0 { 71 break 72 } 73 } 74 if len(certs) == 0 { 75 return nil, nil, fmt.Errorf("no PEM encoded X.509 certificates parsed") 76 } 77 return certs, rootCertBytes, nil 78 } 79 80 // ParsePemEncodedCSR constructs a `x509.CertificateRequest` object using the 81 // given PEM-encoded certificate signing request. 82 func ParsePemEncodedCSR(csrBytes []byte) (*x509.CertificateRequest, error) { 83 block, _ := pem.Decode(csrBytes) 84 if block == nil { 85 return nil, fmt.Errorf("certificate signing request is not properly encoded") 86 } 87 csr, err := x509.ParseCertificateRequest(block.Bytes) 88 if err != nil { 89 return nil, fmt.Errorf("failed to parse X.509 certificate signing request") 90 } 91 return csr, nil 92 } 93 94 // ParsePemEncodedKey takes a PEM-encoded key and parsed the bytes into a `crypto.PrivateKey`. 95 func ParsePemEncodedKey(keyBytes []byte) (crypto.PrivateKey, error) { 96 kb, _ := pem.Decode(keyBytes) 97 if kb == nil { 98 return nil, fmt.Errorf("invalid PEM-encoded key") 99 } 100 101 switch kb.Type { 102 case blockTypeECPrivateKey: 103 key, err := x509.ParseECPrivateKey(kb.Bytes) 104 if err != nil { 105 return nil, fmt.Errorf("failed to parse the ECDSA private key: %v", err) 106 } 107 return key, nil 108 case blockTypeRSAPrivateKey: 109 key, err := x509.ParsePKCS1PrivateKey(kb.Bytes) 110 if err != nil { 111 return nil, fmt.Errorf("failed to parse the RSA private key: %v", err) 112 } 113 return key, nil 114 case blockTypePKCS8PrivateKey: 115 key, err := x509.ParsePKCS8PrivateKey(kb.Bytes) 116 if err != nil { 117 return nil, fmt.Errorf("failed to parse the PKCS8 private key: %v", err) 118 } 119 return key, nil 120 default: 121 return nil, fmt.Errorf("unsupported PEM block type for a private key: %s", kb.Type) 122 } 123 } 124 125 // GetRSAKeySize returns the size if it is RSA key, otherwise it returns an error. 126 func GetRSAKeySize(privKey crypto.PrivateKey) (int, error) { 127 if t := reflect.TypeOf(privKey); t != reflect.TypeOf(&rsa.PrivateKey{}) { 128 return 0, fmt.Errorf("key type is not RSA: %v", t) 129 } 130 pkey := privKey.(*rsa.PrivateKey) 131 return pkey.N.BitLen(), nil 132 } 133 134 // GetEllipticCurve returns the type of curve associated with the private key; 135 // if ECDSA is used, then only 384 and 256 (default) are returned; if non-ECDSA 136 // is used then an error is returned 137 func GetEllipticCurve(privKey *crypto.PrivateKey) (elliptic.Curve, error) { 138 switch key := (*privKey).(type) { 139 // this should agree with var SupportedECSignatureAlgorithms 140 case *ecdsa.PrivateKey: 141 if key.Curve == elliptic.P384() { 142 return key.Curve, nil 143 } 144 return elliptic.P256(), nil 145 default: 146 return nil, fmt.Errorf("private key is not ECDSA based") 147 } 148 } 149 150 // PemCertBytestoString: takes an array of PEM certs in bytes and returns a string array in the same order with 151 // trailing newline characters removed 152 func PemCertBytestoString(caCerts []byte) []string { 153 certs := []string{} 154 var cert string 155 pemBlock := caCerts 156 for block, rest := pem.Decode(pemBlock); block != nil && len(block.Bytes) != 0; block, rest = pem.Decode(pemBlock) { 157 if len(rest) == 0 { 158 cert = strings.TrimPrefix(strings.TrimSuffix(string(pemBlock), "\n"), "\n") 159 certs = append(certs, cert) 160 break 161 } 162 cert = string(pemBlock[0 : len(pemBlock)-len(rest)]) 163 cert = strings.TrimPrefix(strings.TrimSuffix(cert, "\n"), "\n") 164 certs = append(certs, cert) 165 pemBlock = rest 166 } 167 return certs 168 }