I'd like to verify a PEM certificate against an issuing chain which is also a .pem file with several certificates separated by newline characters as shown in this gist, https://gist.github.com/kurtpeek/8bf3282e344c781a20c5deadac75059f. I've tried this with Certpool.AppendCertsFromPEM as follows:
package main
import (
"crypto/x509"
"encoding/pem"
"io/ioutil"
"github.com/sirupsen/logrus"
)
func main() {
caCertPEM, err := ioutil.ReadFile("issuing_chain.pem")
if err != nil {
logrus.WithError(err).Fatal("read CA PEM file")
}
certPEM, err := ioutil.ReadFile("3007e750-e769-440b-9075-41dc2b5b1787.pem")
if err != nil {
logrus.WithError(err).Fatal("read cert PEM file")
}
block, rest := pem.Decode(certPEM)
if block == nil {
logrus.WithField("rest", rest).Fatal("Decode CA PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
logrus.WithError(err).Fatal("parse certificate")
}
roots := x509.NewCertPool()
roots.AppendCertsFromPEM(caCertPEM)
chain, err := cert.Verify(x509.VerifyOptions{Roots: roots})
if err != nil {
logrus.WithError(err).Fatal("failed to verify cert")
}
logrus.Infof("issuing chain: %+v", chain)
}
However, if I run this I get the following error:
FATA[0000] failed to verify cert error="x509: certificate specifies an incompatible key usage"
exit status 1
I believe this error is returned on line 790 of https://golang.org/src/crypto/x509/verify.go:
if len(chains) == 0 {
return nil, CertificateInvalidError{c, IncompatibleUsage, ""}
}
In other words, the Verify() method is unable to build any chains from the options provided. I've tried splitting out the intermediates (the top two in the issuing_chain.pem shown in the gist) into a separate PEM file and adding those as Intermediates to the x509.VerifyOptions, but I still get the same error.
What is the correct way to verify a certificate against an issuing chain in Go?
Your leaf certificate is for client authentication only.
$ openssl x509 -noout -text -in leaf.pem | grep -A1 'Key Usage'
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication
If this is intentional, you must specify the KeyUsages option because "an empty list means ExtKeyUsageServerAuth". You also have to go back to your version of the code that provides the intermediate certs separately:
chain, err := cert.Verify(x509.VerifyOptions{
Roots: roots,
Intermediates: inters,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
Try it on the playground: https://play.golang.org/p/1BNLthzu5Tz. Note that the playground requires the CurrentTime option to verify correctly. Remove this when copying elsewhere!
Related
I'm trying to generate keypair with openpgp lib and when I want to test it by encrypting a test string, it returns the following error openpgp: invalid argument: cannot encrypt because no candidate hash functions are compiled in. (Wanted RIPEMD160 in this case.). However it works when I pass a public key exported from gpg.
Also I'm wondering how to encrypt the private key like gpg --generate-key does?
func main() {
var e *openpgp.Entity
var pubKey *bytes.Buffer
e, _ = openpgp.NewEntity("testUser", "test", "test#test.test", nil)
for _, id := range e.Identities {
err := id.SelfSignature.SignUserId(id.UserId.Id, e.PrimaryKey, e.PrivateKey, nil)
if err != nil {
fmt.Println(err)
return
}
}
buf := new(bytes.Buffer)
w, err := armor.Encode(buf, openpgp.PublicKeyType, nil)
if err != nil {
fmt.Println(err)
return
}
e.Serialize(w)
w.Close()
pubKey = buf
// Encrypting test with public key
entity, err := openpgp.ReadArmoredKeyRing(pubKey)
if err != nil {
fmt.Println(err)
return
}
buf = new(bytes.Buffer)
encoderWriter, err := armor.Encode(buf, "PGP MESSAGE", make(map[string]string))
if err != nil {
fmt.Println(err)
return
}
encryptorWriter, err := openpgp.Encrypt(encoderWriter, entity, nil, nil, nil)
if err != nil {
fmt.Println(err)
return
}
encryptorWriter.Write([]byte("hello world"))
encryptorWriter.Close()
encoderWriter.Close()
fmt.Println(buf.String())
}
I had the exact same error.
TL; DR
It seems that it's an abandoned bug of the golang.org/x/crypto/openpgp package.
golang.org/x/crypto/openpgp package is frozen and deprecated. (= wontfix)
Use a patched fork package instead.
github.com/ProtonMail/go-crypto package # GitHub
TS; DR
Since the official "golang.org/x/crypto/openpgp" package was frozen and deprecated, as long as we use the "golang.org/x/crypto/openpgp" package, it seems that the only current workaround is to either;
Downgrade the Go version and the package, then blank import "_ golang.org/x/crypto/ripemd160" as #mh-cbon mentioned.
Patch the rejected PR on your own. (Rejected due to the freezing of x/crypto/openpgp package)
Patch: https://github.com/golang/crypto/pull/128/files
PR: Use correct default hashes and default ciphers when no preferences given
But I had to implement an OpenPGP key pair generator on Go 1.16.6. Don't ask why...
So, my current alternative was to use the forked package. Which was one of the abounding forks that the Go team mentioned as a sample.
github.com/ProtonMail/go-crypto package # GitHub
go get github.com/ProtonMail/go-crypto
Remove golang.org/x/crypto/openpgp from go.mod
Replace all the "golang.org/x/crypto/openpgp" to "github.com/ProtonMail/go-crypto/openpgp" in the source code.
go mod tidy
References
"x/crypto/openpgp: mark as frozen and deprecated" | Issue #44226 | go | golang # GitHub
"x/crypto/openpgp: new entities cannot be encrypted to by default" | Issue #37646 | go | golang # GitHub
"x/crypto/openpgp: new entities cannot be encrypted to by default" | Issue #12153 | go | golang # GitHub
I have a cert file and a key file where the key file is encrypted with a password. I am trying to decrypt the key file programmatically before loading the key pair.
// Key file and cert file path
cf := filepath.Join(certPath, certFile)
kf := filepath.Join(certPath, keyFile)
//Read & decode the encrypted key file with the pass to make tls work
keyIn, err := ioutil.ReadFile(kf)
if err != nil {
log.Error("Read key error", err)
return nil, err
}
// Decode and decrypt our PEM block
decodedPEM, _ := pem.Decode([]byte(keyIn))
decrypedPemBlock, err := x509.DecryptPEMBlock(decodedPEM, []byte("somepassword"))
if err != nil {
log.Error("decrypt key error", err)
return nil, err
}
// Load our decrypted key pair
crt, err := tls.LoadX509KeyPair(cf, string(decrypedPemBlock))
if err != nil {
log.Error("load key pair error", err)
return nil, err
}
The original certs and key were generated using the following openssl parameters
openssl req -new -newkey rsa:2048 -x509 -keyout $CA_CERT.key -out $CA_CERT -days $validity -passin "pass:$password" -passout "pass:$password" -subj "/C=$C/ST=$ST/L=$L/O=$O/CN=$CN/emailAddress=$EMAIL"
The variables are substituted accordingly with $password being "somepassword"
I have tried decrypting the key using openssl rsa from the command line and it works fine with the the password above.
However in Go I get an error in tls.LoadX509KeyPair saying invalid argument.
time="2018-01-17T18:57:40Z" level=error msg="load key pair error: open
My best guess is that the key encoding might be messed up and I was wondering if anything is wrong with my code.
Update: Added error message and it seems like tls.LoadX509KeyPair can't understand the format as comments have pointed out below.
I only have a PublicKey string, How do I get the PublicKey Fingerprint?
I have got some idea form https://go-review.googlesource.com/c/crypto/+/32814, but I do not know how to
implement ssh.PublicKey interface.
You probably want to use ssh.ParseAuthorizedKey from the ssh package to load the key:
https://godoc.org/golang.org/x/crypto/ssh#ParseAuthorizedKey
That will give you a public key which you can call ssh.FingerprintLegacyMD5 on in order to get the fingerprint (assuming here you want the md5).
https://godoc.org/golang.org/x/crypto/ssh#FingerprintLegacyMD5
https://godoc.org/golang.org/x/crypto/ssh#FingerprintSHA256
func main() {
// Read a key from a file in authorized keys file line format
// This could be an rsa.pub file or a line from authorized_keys
pubKeyBytes := []byte(`ssh-rsa AAAABMYKEY...ABC me#myplace.local`)
// Parse the key, other info ignored
pk, _, _, _, err := ssh.ParseAuthorizedKey(pubKeyBytes)
if err != nil {
panic(err)
}
// Get the fingerprint
f := ssh.FingerprintLegacyMD5(pk)
// Print the fingerprint
fmt.Printf("%s\n", f)
}
There are two fingerprint functions provided, not sure which one you need.
when I tried to enroll the "admin" user, the second call to CreateCertificatePair failed with the "Signature verification failed" message. BTW, I copied the enrollUser function from the eca_test.go. And those tests under membersrvc/ca package can be passed.
//Phase 2 of the protocol
spi := ecies.NewSPI()
eciesKey, err := spi.NewPrivateKey(nil, encPriv)
if err != nil {
return err
}
ecies, err := spi.NewAsymmetricCipherFromPublicKey(eciesKey)
if err != nil {
return err
}
out, err := ecies.Process(resp.Tok.Tok)
if err != nil {
return err
}
req.Tok.Tok = out
req.Sig = nil
hash := primitives.NewHash()
raw, _ := proto.Marshal(req)
hash.Write(raw)
r, s, err := ecdsa.Sign(rand.Reader, signPriv, hash.Sum(nil))
if err != nil {
return err
}
R, _ := r.MarshalText()
S, _ := s.MarshalText()
req.Sig = &pb.Signature{Type: pb.CryptoType_ECDSA, R: R, S: S}
resp, err = ecapCient.CreateCertificatePair(context.Background(), req)
As Sergey mentioned, CreateCertificatePair requests the creation of a new certificate pair,
and according to the documentation,
During registration, the application sends a request to the certificate authority to verify the user registration and if successful, the CA responds with the user certificates and keys.
Upon successful user authentication, the application will perform user registration with the CA exactly once. If registration is attempted a second time for the same user, an error will result.
This is the reason why the second call to CreateCertificatePair is failing.
If you really want to register a user who has already been registered previously, you need to remove the temporary files ( the client enrollment certificate, enrollment key, transaction certificate chain, etc.) that were created by the CA server process, and to do that, run the following command,
rm -rf /var/hyperledger/production
/var/hyperledger/production is the directory where the certificates received from CA are stored.
Souce: Note on security functionality
CreateCertificatePair requests the creation of a new enrolment certificate pair by the ECA.
"enrolment" certificate is unique and can be created just once per user by ECA
Second call to CreateCertificatePair for the same user will lead to error.
I'm at the moment realy struggeling in finding the error in my code - the task is to encrypt a string into a pgp ASCII armored file - a simple thing one could think.
I use the following function, inspired by this gist:
// pgp encryption using the pgp RSA certificate
// massive thx to https://gist.github.com/jyap808/8250124
func encToFile(secretString string, filename string) (string, error) {
log.Println("Public Keyring: ", publicKeyring)
encryptionType := "PGP MESSAGE"
// Read in public key
keyringFileBuffer, _ := os.Open(publicKeyring)
defer keyringFileBuffer.Close()
entityList, err := openpgp.ReadArmoredKeyRing(keyringFileBuffer)
check(err)
encbuf := bytes.NewBuffer(nil)
w, err := armor.Encode(encbuf, encryptionType, nil) // the encoder somehow makes this into ASCII armor
check(err)
plaintext, err := openpgp.Encrypt(w, entityList, nil, nil, nil)
check(err)
message := []byte(secretString)
_, err = plaintext.Write(message)
plaintext.Close()
w.Close()
// Output encrypted/encoded string
log.Println("Writing Encrypted Secred to: ", filename)
// we write the file into a file
err = ioutil.WriteFile(filename, encbuf.Bytes(), 0644)
check(err)
log.Println("File:\n", encbuf.String())
return encbuf.String(), nil
}
However, the guys on the other end get this error message:
gpg: encrypted with RSA key, ID 5BE299DC
gpg: decryption failed: No secret key
Hints and suggestion would be very welcome!
However, the guys on the other end get this error message:
gpg: encrypted with RSA key, ID 5BE299DC
gpg: decryption failed: No secret key
If you encrypted for the right key, I don't think you did anything wrong. Looking at that key on the key servers, you encrypted to the newest (and only) encryption subkey.
If the "guy on the other end" gets an error message indicating that he would not hold the secret key, then either
you use the wrong key for encryption,
"the other guy" gave you the wrong key or
"the other guy" messed up himself.
You can verify what's going wrong by passing the encrypted contents to gpg --list-packets or pgpdump, which list the OpenPGP packets contained in the message and are very helpful at debugging OpenPGP issues.