github.com/letsencrypt/boulder@v0.20251208.0/privatekey/privatekey.go (about) 1 package privatekey 2 3 import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/rand" 7 "crypto/rsa" 8 "crypto/sha256" 9 "crypto/x509" 10 "encoding/pem" 11 "errors" 12 "fmt" 13 "hash" 14 "os" 15 ) 16 17 func makeVerifyHash() (hash.Hash, error) { 18 randBytes := make([]byte, 32) 19 _, err := rand.Read(randBytes) 20 if err != nil { 21 return nil, err 22 } 23 24 hash := sha256.New() 25 _, err = hash.Write(randBytes) 26 if err != nil { 27 return nil, err 28 } 29 return hash, nil 30 } 31 32 // verifyRSA is broken out of Verify for testing purposes. 33 func verifyRSA(privKey *rsa.PrivateKey, pubKey *rsa.PublicKey, msgHash hash.Hash) (crypto.Signer, crypto.PublicKey, error) { 34 signatureRSA, err := rsa.SignPSS(rand.Reader, privKey, crypto.SHA256, msgHash.Sum(nil), nil) 35 if err != nil { 36 return nil, nil, fmt.Errorf("failed to sign using the provided RSA private key: %s", err) 37 } 38 39 err = rsa.VerifyPSS(pubKey, crypto.SHA256, msgHash.Sum(nil), signatureRSA, nil) 40 if err != nil { 41 return nil, nil, fmt.Errorf("the provided RSA private key failed signature verification: %s", err) 42 } 43 return privKey, privKey.Public(), nil 44 } 45 46 // verifyECDSA is broken out of Verify for testing purposes. 47 func verifyECDSA(privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey, msgHash hash.Hash) (crypto.Signer, crypto.PublicKey, error) { 48 r, s, err := ecdsa.Sign(rand.Reader, privKey, msgHash.Sum(nil)) 49 if err != nil { 50 return nil, nil, fmt.Errorf("failed to sign using the provided ECDSA private key: %s", err) 51 } 52 53 verify := ecdsa.Verify(pubKey, msgHash.Sum(nil), r, s) 54 if !verify { 55 return nil, nil, errors.New("the provided ECDSA private key failed signature verification") 56 } 57 return privKey, privKey.Public(), nil 58 } 59 60 // verify ensures that the embedded PublicKey of the provided privateKey is 61 // actually a match for the private key. For an example of private keys 62 // embedding a mismatched public key, see: 63 // https://blog.hboeck.de/archives/888-How-I-tricked-Symantec-with-a-Fake-Private-Key.html. 64 func verify(privateKey crypto.Signer) (crypto.Signer, crypto.PublicKey, error) { 65 verifyHash, err := makeVerifyHash() 66 if err != nil { 67 return nil, nil, err 68 } 69 70 switch k := privateKey.(type) { 71 case *rsa.PrivateKey: 72 return verifyRSA(k, &k.PublicKey, verifyHash) 73 74 case *ecdsa.PrivateKey: 75 return verifyECDSA(k, &k.PublicKey, verifyHash) 76 77 default: 78 // This should never happen. 79 return nil, nil, errors.New("the provided private key could not be asserted to ECDSA or RSA") 80 } 81 } 82 83 // Load decodes and parses a private key from the provided file path and returns 84 // the private key as crypto.Signer. keyPath is expected to be a PEM formatted 85 // RSA or ECDSA private key in a PKCS #1, PKCS# 8, or SEC 1 container. The 86 // embedded PublicKey of the provided private key will be verified as an actual 87 // match for the private key and returned as a crypto.PublicKey. This function 88 // is only intended for use in administrative tooling and tests. 89 func Load(keyPath string) (crypto.Signer, crypto.PublicKey, error) { 90 keyBytes, err := os.ReadFile(keyPath) 91 if err != nil { 92 return nil, nil, fmt.Errorf("could not read key file %q", keyPath) 93 } 94 95 var keyDER *pem.Block 96 for { 97 keyDER, keyBytes = pem.Decode(keyBytes) 98 if keyDER == nil || keyDER.Type != "EC PARAMETERS" { 99 break 100 } 101 } 102 if keyDER == nil { 103 return nil, nil, fmt.Errorf("no PEM formatted block found in %q", keyPath) 104 } 105 106 // Attempt to parse the PEM block as a private key in a PKCS #8 container. 107 signer, err := x509.ParsePKCS8PrivateKey(keyDER.Bytes) 108 if err == nil { 109 cryptoSigner, ok := signer.(crypto.Signer) 110 if ok { 111 return verify(cryptoSigner) 112 } 113 } 114 115 // Attempt to parse the PEM block as a private key in a PKCS #1 container. 116 rsaSigner, err := x509.ParsePKCS1PrivateKey(keyDER.Bytes) 117 if err != nil && keyDER.Type == "RSA PRIVATE KEY" { 118 return nil, nil, fmt.Errorf("unable to parse %q as a PKCS#1 RSA private key: %w", keyPath, err) 119 } 120 if err == nil { 121 return verify(rsaSigner) 122 } 123 124 // Attempt to parse the PEM block as a private key in a SEC 1 container. 125 ecdsaSigner, err := x509.ParseECPrivateKey(keyDER.Bytes) 126 if err == nil { 127 return verify(ecdsaSigner) 128 } 129 return nil, nil, fmt.Errorf("unable to parse %q as a private key", keyPath) 130 }