util/certificates: Add CSR utility function.

This commit is contained in:
George Tankersley 2016-08-02 17:33:22 -07:00 committed by Yifan Gu
parent 9d2a5fe5e8
commit 5801fa5f4d
3 changed files with 129 additions and 0 deletions

View File

@ -17,9 +17,18 @@ limitations under the License.
package certificates
import (
"crypto/ecdsa"
"crypto/elliptic"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"k8s.io/kubernetes/pkg/apis/certificates"
)
@ -38,3 +47,96 @@ func ParseCertificateRequestObject(obj *certificates.CertificateSigningRequest)
}
return csr, nil
}
// NewCertificateRequest generates a PEM-encoded CSR using the supplied private
// key file, subject, and SANs. If the private key file does not exist, it generates a
// new ECDSA P256 key to use and writes it to the keyFile path.
func NewCertificateRequest(keyFile string, subject *pkix.Name, dnsSANs []string, ipSANs []net.IP) ([]byte, error) {
var privateKey interface{}
if _, err := os.Stat(keyFile); os.IsNotExist(err) {
privateKey, err = ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
if err != nil {
return nil, err
}
ecdsaKey := privateKey.(*ecdsa.PrivateKey)
derBytes, err := x509.MarshalECPrivateKey(ecdsaKey)
if err != nil {
return nil, err
}
pemBlock := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: derBytes,
}
err = ioutil.WriteFile(keyFile, pem.EncodeToMemory(pemBlock), os.FileMode(0600))
if err != nil {
return nil, err
}
}
keyBytes, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, err
}
var block *pem.Block
var sigType x509.SignatureAlgorithm
block, _ = pem.Decode(keyBytes)
switch block.Type {
case "EC PRIVATE KEY":
privateKey, err = x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, err
}
ecdsaKey := privateKey.(*ecdsa.PrivateKey)
switch ecdsaKey.Curve.Params().BitSize {
case 521:
sigType = x509.ECDSAWithSHA512
case 384:
sigType = x509.ECDSAWithSHA384
default:
sigType = x509.ECDSAWithSHA256
}
case "RSA PRIVATE KEY":
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
rsaKey := privateKey.(*rsa.PrivateKey)
keySize := rsaKey.N.BitLen()
switch {
case keySize >= 4096:
sigType = x509.SHA512WithRSA
case keySize >= 3072:
sigType = x509.SHA384WithRSA
default:
sigType = x509.SHA256WithRSA
}
default:
return nil, fmt.Errorf("unsupported key type: %s", block.Type)
}
template := &x509.CertificateRequest{
Subject: *subject,
SignatureAlgorithm: sigType,
DNSNames: dnsSANs,
IPAddresses: ipSANs,
}
csr, err := x509.CreateCertificateRequest(cryptorand.Reader, template, privateKey)
if err != nil {
return nil, err
}
pemBlock := &pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr,
}
return pem.EncodeToMemory(pemBlock), nil
}

View File

@ -0,0 +1,21 @@
package certificates
import (
"crypto/x509/pkix"
"net"
"testing"
)
func TestNewCertificateRequest(t *testing.T) {
keyFile := "testdata/dontUseThisKey.pem"
subject := &pkix.Name{
CommonName: "kube-worker",
}
dnsSANs := []string{"localhost"}
ipSANs := []net.IP{net.ParseIP("127.0.0.1")}
_, err := NewCertificateRequest(keyFile, subject, dnsSANs, ipSANs)
if err != nil {
t.Error(err)
}
}

View File

@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAPEbSXwyDfWf0+61Oofd7aHkmdX69mrzD2Xb1CHF5syfsoRIhnG0dJ
ozBulPZCDDWgBwYFK4EEACKhZANiAATjlMJAtKhEPqU/i7MsrgKcK/RmXHC6He7W
0p69+9qFXg2raJ9zvvbKxkiu2ELOYRDAz0utcFTBOIgoUJEzBVmsjZQ7dvFa1BKP
Ym7MFAKG3O2espBqXn+audgdHGh5B0I=
-----END EC PRIVATE KEY-----