github.com/goreleaser/nfpm/v2@v2.44.0/internal/sign/rsa.go (about) 1 package sign 2 3 import ( 4 "crypto" 5 "crypto/rand" 6 "crypto/rsa" 7 "crypto/sha1" // nolint:gosec 8 "crypto/x509" 9 "encoding/pem" 10 "errors" 11 "fmt" 12 "io" 13 "os" 14 ) 15 16 var ( 17 errNoPemBlock = errors.New("no PEM block found") 18 errDigestNotSH1 = errors.New("digest is not a SHA1 hash") 19 errNoPassphrase = errors.New("key is encrypted but no passphrase was provided") 20 errNoRSAKey = errors.New("key is not an RSA key") 21 ) 22 23 const ( 24 PKCS1PrivkeyPreamble = "RSA PRIVATE KEY" 25 PKCS8PrivkeyPreamble = "PRIVATE KEY" 26 ) 27 28 // RSASignSHA1Digest signs the provided SHA1 message digest. The key file 29 // must be in the PEM format and can either be encrypted or not. 30 func RSASignSHA1Digest(sha1Digest []byte, keyFile, passphrase string) ([]byte, error) { 31 if len(sha1Digest) != sha1.Size { 32 return nil, errDigestNotSH1 33 } 34 35 keyFileContent, err := os.ReadFile(keyFile) 36 if err != nil { 37 return nil, fmt.Errorf("reading key file: %w", err) 38 } 39 40 block, _ := pem.Decode(keyFileContent) 41 if block == nil { 42 return nil, errNoPemBlock 43 } 44 45 blockData := block.Bytes 46 if x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck 47 if passphrase == "" { 48 return nil, errNoPassphrase 49 } 50 51 var decryptedBlockData []byte 52 53 decryptedBlockData, err = x509.DecryptPEMBlock(block, []byte(passphrase)) //nolint:staticcheck 54 if err != nil { 55 return nil, fmt.Errorf("decrypt private key PEM block: %w", err) 56 } 57 58 blockData = decryptedBlockData 59 } 60 61 var priv crypto.Signer 62 63 switch block.Type { 64 case PKCS1PrivkeyPreamble: 65 priv, err = x509.ParsePKCS1PrivateKey(blockData) 66 if err != nil { 67 return nil, fmt.Errorf("parse PKCS#1 private key: %w", err) 68 } 69 case PKCS8PrivkeyPreamble: 70 privAny, err := x509.ParsePKCS8PrivateKey(blockData) 71 if err != nil { 72 return nil, fmt.Errorf("parse PKCS#8 private key: %w", err) 73 } 74 privTmp, ok := privAny.(crypto.Signer) 75 if !ok { 76 return nil, fmt.Errorf("cannot sign with given private key") 77 } 78 priv = privTmp 79 default: 80 return nil, fmt.Errorf(`key type "%v" is not supported`, block.Type) 81 } 82 83 signature, err := priv.Sign(rand.Reader, sha1Digest, crypto.SHA1) 84 if err != nil { 85 return nil, fmt.Errorf("signing: %w", err) 86 } 87 88 return signature, nil 89 } 90 91 func rsaSign(message io.Reader, keyFile, passphrase string) ([]byte, error) { 92 sha1Hash := sha1.New() // nolint:gosec 93 _, err := io.Copy(sha1Hash, message) 94 if err != nil { 95 return nil, fmt.Errorf("create SHA1 message digest: %w", err) 96 } 97 98 return RSASignSHA1Digest(sha1Hash.Sum(nil), keyFile, passphrase) 99 } 100 101 // RSAVerifySHA1Digest is exported for use in tests and verifies a signature over the 102 // provided SHA1 hash of a message. The key file must be in the PEM format. 103 func RSAVerifySHA1Digest(sha1Digest, signature []byte, publicKeyFile string) error { 104 if len(sha1Digest) != sha1.Size { 105 return errDigestNotSH1 106 } 107 108 keyFileContent, err := os.ReadFile(publicKeyFile) 109 if err != nil { 110 return fmt.Errorf("reading key file: %w", err) 111 } 112 113 block, _ := pem.Decode(keyFileContent) 114 if block == nil { 115 return errNoPemBlock 116 } 117 118 pub, err := x509.ParsePKIXPublicKey(block.Bytes) 119 if err != nil { 120 return fmt.Errorf("parse PKIX public key: %w", err) 121 } 122 123 rsaPub, ok := pub.(*rsa.PublicKey) 124 if !ok { 125 return errNoRSAKey 126 } 127 128 err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA1, sha1Digest, signature) 129 if err != nil { 130 return fmt.Errorf("verify PKCS1v15 signature: %w", err) 131 } 132 133 return nil 134 } 135 136 func rsaVerify(message io.Reader, signature []byte, publicKeyFile string) error { 137 sha1Hash := sha1.New() // nolint:gosec 138 _, err := io.Copy(sha1Hash, message) 139 if err != nil { 140 return fmt.Errorf("create SHA1 message digest: %w", err) 141 } 142 143 return RSAVerifySHA1Digest(sha1Hash.Sum(nil), signature, publicKeyFile) 144 }