golang/go

x/crypto/ssh: cannot sign certificate with different algorithm

stoggi opened this issue · 5 comments

What version of Go are you using (go version)?

$ go version
go version go1.13.4 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
not relevant

What did you do?

I want to be able to change the algorithm used when signing an SSH certificate. Specifically with a private key implemented in hardware with ssh.NewSignerFromSigner.

What did you expect to see?

I should be able to pass an AlgorithmSigner interface, and specify which algorithm to use when invoking Certificate.SignCert

https://github.com/golang/crypto/blob/0a08dada0ff98d02f3864a23ae8d27cb8fba5303/ssh/certs.go#L417-L432

What did you see instead?

When signing a certificate with Certificate.SignCert I can not specify the algorithm used.

I am happy to contribute a change to fix this. I propose adding a new method to the Certificate struct, named SignCertWithAlgorithm which correctly calls authority.SignWithAlgorithm.

I've put together a short proof-of-concept implementing SignCertWithAlgorithm and adding a new private method prepareForSigning to keep the nonce generation in once place.

https://play.golang.org/p/PYbYu6-9XY8

An alternative implementation would be to create a NewAlgorithmSignerFromSigner method to return a signer interface, that can sign with a particular algorithm. Then you could pass it to Certificate.SignCert normally.

https://play.golang.org/p/lywpKtapMeZ

type sshAlgorithmSigner struct {
	algorithm string
	signer    ssh.AlgorithmSigner
}

func (s *sshAlgorithmSigner) PublicKey() ssh.PublicKey {
	return s.signer.PublicKey()
}

func (s *sshAlgorithmSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
	return s.signer.SignWithAlgorithm(rand, data, s.algorithm)
}

func NewAlgorithmSignerFromSigner(signer crypto.Signer, algorithm string) (ssh.Signer, error) {
	sshSigner, err := ssh.NewSignerFromSigner(signer)
	if err != nil {
		return nil, err
	}
	algorithmSigner, ok := sshSigner.(ssh.AlgorithmSigner)
	if !ok {
		return nil, errors.New("unable to cast to ssh.AlgorithmSigner")
	}
	s := sshAlgorithmSigner{
		signer:    algorithmSigner,
		algorithm: algorithm,
	}
	return &s, nil
}
alex commented

A particular urgency for this issue is that right now with an RSA private key, the default is SHA-1. OpenSSH 8.2 has removed support for RSA-SHA1 signatures on certificates: https://www.openssh.com/txt/release-8.2

Change https://go.dev/cl/508398 mentions this issue: ssh: add MultiAlgorithmSigner