1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
27 "golang.org/x/crypto/ed25519"
30 // These constants represent the algorithm names for key types supported by this
33 KeyAlgoRSA = "ssh-rsa"
34 KeyAlgoDSA = "ssh-dss"
35 KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
36 KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
37 KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
38 KeyAlgoED25519 = "ssh-ed25519"
41 // These constants represent non-default signature algorithms that are supported
42 // as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See
43 // [PROTOCOL.agent] section 4.5.1 and
44 // https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10
46 SigAlgoRSA = "ssh-rsa"
47 SigAlgoRSASHA2256 = "rsa-sha2-256"
48 SigAlgoRSASHA2512 = "rsa-sha2-512"
51 // parsePubKey parses a public key of the given algorithm.
52 // Use ParsePublicKey for keys with prepended algorithm.
53 func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) {
59 case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
62 return parseED25519(in)
63 case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
64 cert, err := parseCert(in, certToPrivAlgo(algo))
70 return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
73 // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
74 // (see sshd(8) manual page) once the options and key type fields have been
76 func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) {
77 in = bytes.TrimSpace(in)
79 i := bytes.IndexAny(in, " \t")
85 key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key)))
86 n, err := base64.StdEncoding.Decode(key, base64Key)
91 out, err = ParsePublicKey(key)
95 comment = string(bytes.TrimSpace(in[i:]))
96 return out, comment, nil
99 // ParseKnownHosts parses an entry in the format of the known_hosts file.
101 // The known_hosts format is documented in the sshd(8) manual page. This
102 // function will parse a single entry from in. On successful return, marker
103 // will contain the optional marker value (i.e. "cert-authority" or "revoked")
104 // or else be empty, hosts will contain the hosts that this entry matches,
105 // pubKey will contain the public key and comment will contain any trailing
106 // comment at the end of the line. See the sshd(8) manual page for the various
107 // forms that a host string can take.
109 // The unparsed remainder of the input will be returned in rest. This function
110 // can be called repeatedly to parse multiple entries.
112 // If no entries were found in the input then err will be io.EOF. Otherwise a
113 // non-nil err value indicates a parse error.
114 func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) {
116 end := bytes.IndexByte(in, '\n')
124 end = bytes.IndexByte(in, '\r')
129 in = bytes.TrimSpace(in)
130 if len(in) == 0 || in[0] == '#' {
135 i := bytes.IndexAny(in, " \t")
141 // Strip out the beginning of the known_host key.
142 // This is either an optional marker or a (set of) hostname(s).
143 keyFields := bytes.Fields(in)
144 if len(keyFields) < 3 || len(keyFields) > 5 {
145 return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data")
148 // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated
151 if keyFields[0][0] == '@' {
152 marker = string(keyFields[0][1:])
153 keyFields = keyFields[1:]
156 hosts := string(keyFields[0])
157 // keyFields[1] contains the key type (e.g. “ssh-rsa”).
158 // However, that information is duplicated inside the
159 // base64-encoded key and so is ignored here.
161 key := bytes.Join(keyFields[2:], []byte(" "))
162 if pubKey, comment, err = parseAuthorizedKey(key); err != nil {
163 return "", nil, nil, "", nil, err
166 return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil
169 return "", nil, nil, "", nil, io.EOF
172 // ParseAuthorizedKeys parses a public key from an authorized_keys
173 // file used in OpenSSH according to the sshd(8) manual page.
174 func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
176 end := bytes.IndexByte(in, '\n')
184 end = bytes.IndexByte(in, '\r')
189 in = bytes.TrimSpace(in)
190 if len(in) == 0 || in[0] == '#' {
195 i := bytes.IndexAny(in, " \t")
201 if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
202 return out, comment, options, rest, nil
205 // No key type recognised. Maybe there's an options field at
209 var candidateOptions []string
211 for i, b = range in {
212 isEnd := !inQuote && (b == ' ' || b == '\t')
213 if (b == ',' && !inQuote) || isEnd {
214 if i-optionStart > 0 {
215 candidateOptions = append(candidateOptions, string(in[optionStart:i]))
222 if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) {
226 for i < len(in) && (in[i] == ' ' || in[i] == '\t') {
230 // Invalid line: unmatched quote
236 i = bytes.IndexAny(in, " \t")
242 if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
243 options = candidateOptions
244 return out, comment, options, rest, nil
251 return nil, "", nil, nil, errors.New("ssh: no key found")
254 // ParsePublicKey parses an SSH public key formatted for use in
255 // the SSH wire protocol according to RFC 4253, section 6.6.
256 func ParsePublicKey(in []byte) (out PublicKey, err error) {
257 algo, in, ok := parseString(in)
259 return nil, errShortRead
262 out, rest, err = parsePubKey(in, string(algo))
264 return nil, errors.New("ssh: trailing junk in public key")
270 // MarshalAuthorizedKey serializes key for inclusion in an OpenSSH
271 // authorized_keys file. The return value ends with newline.
272 func MarshalAuthorizedKey(key PublicKey) []byte {
274 b.WriteString(key.Type())
276 e := base64.NewEncoder(base64.StdEncoding, b)
277 e.Write(key.Marshal())
283 // PublicKey is an abstraction of different types of public keys.
284 type PublicKey interface {
285 // Type returns the key's type, e.g. "ssh-rsa".
288 // Marshal returns the serialized key data in SSH wire format,
289 // with the name prefix. To unmarshal the returned data, use
290 // the ParsePublicKey function.
293 // Verify that sig is a signature on the given data using this
294 // key. This function will hash the data appropriately first.
295 Verify(data []byte, sig *Signature) error
298 // CryptoPublicKey, if implemented by a PublicKey,
299 // returns the underlying crypto.PublicKey form of the key.
300 type CryptoPublicKey interface {
301 CryptoPublicKey() crypto.PublicKey
304 // A Signer can create signatures that verify against a public key.
305 type Signer interface {
306 // PublicKey returns an associated PublicKey instance.
307 PublicKey() PublicKey
309 // Sign returns raw signature for the given data. This method
310 // will apply the hash specified for the keytype to the data.
311 Sign(rand io.Reader, data []byte) (*Signature, error)
314 // A AlgorithmSigner is a Signer that also supports specifying a specific
315 // algorithm to use for signing.
316 type AlgorithmSigner interface {
319 // SignWithAlgorithm is like Signer.Sign, but allows specification of a
320 // non-default signing algorithm. See the SigAlgo* constants in this
321 // package for signature algorithms supported by this package. Callers may
322 // pass an empty string for the algorithm in which case the AlgorithmSigner
323 // will use its default algorithm.
324 SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
327 type rsaPublicKey rsa.PublicKey
329 func (r *rsaPublicKey) Type() string {
333 // parseRSA parses an RSA key according to RFC 4253, section 6.6.
334 func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
338 Rest []byte `ssh:"rest"`
340 if err := Unmarshal(in, &w); err != nil {
344 if w.E.BitLen() > 24 {
345 return nil, nil, errors.New("ssh: exponent too large")
348 if e < 3 || e&1 == 0 {
349 return nil, nil, errors.New("ssh: incorrect exponent")
352 var key rsa.PublicKey
355 return (*rsaPublicKey)(&key), w.Rest, nil
358 func (r *rsaPublicKey) Marshal() []byte {
359 e := new(big.Int).SetInt64(int64(r.E))
360 // RSA publickey struct layout should match the struct used by
361 // parseRSACert in the x/crypto/ssh/agent package.
371 return Marshal(&wirekey)
374 func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
379 case SigAlgoRSASHA2256:
381 case SigAlgoRSASHA2512:
384 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type())
389 return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob)
392 func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
393 return (*rsa.PublicKey)(r)
396 type dsaPublicKey dsa.PublicKey
398 func (k *dsaPublicKey) Type() string {
402 func checkDSAParams(param *dsa.Parameters) error {
403 // SSH specifies FIPS 186-2, which only provided a single size
404 // (1024 bits) DSA key. FIPS 186-3 allows for larger key
405 // sizes, which would confuse SSH.
406 if l := param.P.BitLen(); l != 1024 {
407 return fmt.Errorf("ssh: unsupported DSA key size %d", l)
413 // parseDSA parses an DSA key according to RFC 4253, section 6.6.
414 func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
417 Rest []byte `ssh:"rest"`
419 if err := Unmarshal(in, &w); err != nil {
423 param := dsa.Parameters{
428 if err := checkDSAParams(¶m); err != nil {
432 key := &dsaPublicKey{
436 return key, w.Rest, nil
439 func (k *dsaPublicKey) Marshal() []byte {
440 // DSA publickey struct layout should match the struct used by
441 // parseDSACert in the x/crypto/ssh/agent package.
456 func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error {
457 if sig.Format != k.Type() {
458 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
460 h := crypto.SHA1.New()
464 // Per RFC 4253, section 6.6,
465 // The value for 'dss_signature_blob' is encoded as a string containing
466 // r, followed by s (which are 160-bit integers, without lengths or
467 // padding, unsigned, and in network byte order).
468 // For DSS purposes, sig.Blob should be exactly 40 bytes in length.
469 if len(sig.Blob) != 40 {
470 return errors.New("ssh: DSA signature parse error")
472 r := new(big.Int).SetBytes(sig.Blob[:20])
473 s := new(big.Int).SetBytes(sig.Blob[20:])
474 if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) {
477 return errors.New("ssh: signature did not verify")
480 func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey {
481 return (*dsa.PublicKey)(k)
484 type dsaPrivateKey struct {
488 func (k *dsaPrivateKey) PublicKey() PublicKey {
489 return (*dsaPublicKey)(&k.PrivateKey.PublicKey)
492 func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
493 return k.SignWithAlgorithm(rand, data, "")
496 func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
497 if algorithm != "" && algorithm != k.PublicKey().Type() {
498 return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
501 h := crypto.SHA1.New()
504 r, s, err := dsa.Sign(rand, k.PrivateKey, digest)
509 sig := make([]byte, 40)
513 copy(sig[20-len(rb):20], rb)
514 copy(sig[40-len(sb):], sb)
517 Format: k.PublicKey().Type(),
522 type ecdsaPublicKey ecdsa.PublicKey
524 func (k *ecdsaPublicKey) Type() string {
525 return "ecdsa-sha2-" + k.nistID()
528 func (k *ecdsaPublicKey) nistID() string {
529 switch k.Params().BitSize {
537 panic("ssh: unsupported ecdsa key size")
540 type ed25519PublicKey ed25519.PublicKey
542 func (k ed25519PublicKey) Type() string {
543 return KeyAlgoED25519
546 func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
549 Rest []byte `ssh:"rest"`
552 if err := Unmarshal(in, &w); err != nil {
556 key := ed25519.PublicKey(w.KeyBytes)
558 return (ed25519PublicKey)(key), w.Rest, nil
561 func (k ed25519PublicKey) Marshal() []byte {
572 func (k ed25519PublicKey) Verify(b []byte, sig *Signature) error {
573 if sig.Format != k.Type() {
574 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
577 edKey := (ed25519.PublicKey)(k)
578 if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
579 return errors.New("ssh: signature did not verify")
585 func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey {
586 return ed25519.PublicKey(k)
589 func supportedEllipticCurve(curve elliptic.Curve) bool {
590 return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
593 // ecHash returns the hash to match the given elliptic curve, see RFC
594 // 5656, section 6.2.1
595 func ecHash(curve elliptic.Curve) crypto.Hash {
596 bitSize := curve.Params().BitSize
606 // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
607 func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
611 Rest []byte `ssh:"rest"`
614 if err := Unmarshal(in, &w); err != nil {
618 key := new(ecdsa.PublicKey)
622 key.Curve = elliptic.P256()
624 key.Curve = elliptic.P384()
626 key.Curve = elliptic.P521()
628 return nil, nil, errors.New("ssh: unsupported curve")
631 key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes)
632 if key.X == nil || key.Y == nil {
633 return nil, nil, errors.New("ssh: invalid curve point")
635 return (*ecdsaPublicKey)(key), w.Rest, nil
638 func (k *ecdsaPublicKey) Marshal() []byte {
639 // See RFC 5656, section 3.1.
640 keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y)
641 // ECDSA publickey struct layout should match the struct used by
642 // parseECDSACert in the x/crypto/ssh/agent package.
656 func (k *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
657 if sig.Format != k.Type() {
658 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
661 h := ecHash(k.Curve).New()
665 // Per RFC 5656, section 3.1.2,
666 // The ecdsa_signature_blob value has the following specific encoding:
674 if err := Unmarshal(sig.Blob, &ecSig); err != nil {
678 if ecdsa.Verify((*ecdsa.PublicKey)(k), digest, ecSig.R, ecSig.S) {
681 return errors.New("ssh: signature did not verify")
684 func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
685 return (*ecdsa.PublicKey)(k)
688 // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
689 // *ecdsa.PrivateKey or any other crypto.Signer and returns a
690 // corresponding Signer instance. ECDSA keys must use P-256, P-384 or
691 // P-521. DSA keys must use parameter size L1024N160.
692 func NewSignerFromKey(key interface{}) (Signer, error) {
693 switch key := key.(type) {
695 return NewSignerFromSigner(key)
696 case *dsa.PrivateKey:
697 return newDSAPrivateKey(key)
699 return nil, fmt.Errorf("ssh: unsupported key type %T", key)
703 func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) {
704 if err := checkDSAParams(&key.PublicKey.Parameters); err != nil {
708 return &dsaPrivateKey{key}, nil
711 type wrappedSigner struct {
716 // NewSignerFromSigner takes any crypto.Signer implementation and
717 // returns a corresponding Signer interface. This can be used, for
718 // example, with keys kept in hardware modules.
719 func NewSignerFromSigner(signer crypto.Signer) (Signer, error) {
720 pubKey, err := NewPublicKey(signer.Public())
725 return &wrappedSigner{signer, pubKey}, nil
728 func (s *wrappedSigner) PublicKey() PublicKey {
732 func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
733 return s.SignWithAlgorithm(rand, data, "")
736 func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
737 var hashFunc crypto.Hash
739 if _, ok := s.pubKey.(*rsaPublicKey); ok {
740 // RSA keys support a few hash functions determined by the requested signature algorithm
743 algorithm = SigAlgoRSA
744 hashFunc = crypto.SHA1
745 case SigAlgoRSASHA2256:
746 hashFunc = crypto.SHA256
747 case SigAlgoRSASHA2512:
748 hashFunc = crypto.SHA512
750 return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
753 // The only supported algorithm for all other key types is the same as the type of the key
755 algorithm = s.pubKey.Type()
756 } else if algorithm != s.pubKey.Type() {
757 return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
760 switch key := s.pubKey.(type) {
762 hashFunc = crypto.SHA1
763 case *ecdsaPublicKey:
764 hashFunc = ecHash(key.Curve)
765 case ed25519PublicKey:
767 return nil, fmt.Errorf("ssh: unsupported key type %T", key)
780 signature, err := s.signer.Sign(rand, digest, hashFunc)
785 // crypto.Signer.Sign is expected to return an ASN.1-encoded signature
786 // for ECDSA and DSA, but that's not the encoding expected by SSH, so
788 switch s.pubKey.(type) {
789 case *ecdsaPublicKey, *dsaPublicKey:
790 type asn1Signature struct {
793 asn1Sig := new(asn1Signature)
794 _, err := asn1.Unmarshal(signature, asn1Sig)
799 switch s.pubKey.(type) {
800 case *ecdsaPublicKey:
801 signature = Marshal(asn1Sig)
804 signature = make([]byte, 40)
805 r := asn1Sig.R.Bytes()
806 s := asn1Sig.S.Bytes()
807 copy(signature[20-len(r):20], r)
808 copy(signature[40-len(s):40], s)
818 // NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
819 // or ed25519.PublicKey returns a corresponding PublicKey instance.
820 // ECDSA keys must use P-256, P-384 or P-521.
821 func NewPublicKey(key interface{}) (PublicKey, error) {
822 switch key := key.(type) {
824 return (*rsaPublicKey)(key), nil
825 case *ecdsa.PublicKey:
826 if !supportedEllipticCurve(key.Curve) {
827 return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported")
829 return (*ecdsaPublicKey)(key), nil
831 return (*dsaPublicKey)(key), nil
832 case ed25519.PublicKey:
833 return (ed25519PublicKey)(key), nil
835 return nil, fmt.Errorf("ssh: unsupported key type %T", key)
839 // ParsePrivateKey returns a Signer from a PEM encoded private key. It supports
840 // the same keys as ParseRawPrivateKey.
841 func ParsePrivateKey(pemBytes []byte) (Signer, error) {
842 key, err := ParseRawPrivateKey(pemBytes)
847 return NewSignerFromKey(key)
850 // ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private
851 // key and passphrase. It supports the same keys as
852 // ParseRawPrivateKeyWithPassphrase.
853 func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) {
854 key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase)
859 return NewSignerFromKey(key)
862 // encryptedBlock tells whether a private key is
863 // encrypted by examining its Proc-Type header
864 // for a mention of ENCRYPTED
865 // according to RFC 1421 Section 4.6.1.1.
866 func encryptedBlock(block *pem.Block) bool {
867 return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED")
870 // ParseRawPrivateKey returns a private key from a PEM encoded private key. It
871 // supports RSA (PKCS#1), PKCS#8, DSA (OpenSSL), and ECDSA private keys.
872 func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
873 block, _ := pem.Decode(pemBytes)
875 return nil, errors.New("ssh: no key found")
878 if encryptedBlock(block) {
879 return nil, errors.New("ssh: cannot decode encrypted private keys")
883 case "RSA PRIVATE KEY":
884 return x509.ParsePKCS1PrivateKey(block.Bytes)
885 // RFC5208 - https://tools.ietf.org/html/rfc5208
887 return x509.ParsePKCS8PrivateKey(block.Bytes)
888 case "EC PRIVATE KEY":
889 return x509.ParseECPrivateKey(block.Bytes)
890 case "DSA PRIVATE KEY":
891 return ParseDSAPrivateKey(block.Bytes)
892 case "OPENSSH PRIVATE KEY":
893 return parseOpenSSHPrivateKey(block.Bytes)
895 return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
899 // ParseRawPrivateKeyWithPassphrase returns a private key decrypted with
900 // passphrase from a PEM encoded private key. If wrong passphrase, return
901 // x509.IncorrectPasswordError.
902 func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) {
903 block, _ := pem.Decode(pemBytes)
905 return nil, errors.New("ssh: no key found")
909 if encryptedBlock(block) {
910 if x509.IsEncryptedPEMBlock(block) {
912 buf, err = x509.DecryptPEMBlock(block, passPhrase)
914 if err == x509.IncorrectPasswordError {
917 return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
923 case "RSA PRIVATE KEY":
924 return x509.ParsePKCS1PrivateKey(buf)
925 case "EC PRIVATE KEY":
926 return x509.ParseECPrivateKey(buf)
927 case "DSA PRIVATE KEY":
928 return ParseDSAPrivateKey(buf)
929 case "OPENSSH PRIVATE KEY":
930 return parseOpenSSHPrivateKey(buf)
932 return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
936 // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
937 // specified by the OpenSSL DSA man page.
938 func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
947 rest, err := asn1.Unmarshal(der, &k)
949 return nil, errors.New("ssh: failed to parse DSA key: " + err.Error())
952 return nil, errors.New("ssh: garbage after DSA key")
955 return &dsa.PrivateKey{
956 PublicKey: dsa.PublicKey{
957 Parameters: dsa.Parameters{
968 // Implemented based on the documentation at
969 // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
970 func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
971 const magic = "openssh-key-v1\x00"
972 if len(key) < len(magic) || string(key[:len(magic)]) != magic {
973 return nil, errors.New("ssh: invalid openssh private key format")
975 remaining := key[len(magic):]
986 if err := Unmarshal(remaining, &w); err != nil {
990 if w.KdfName != "none" || w.CipherName != "none" {
991 return nil, errors.New("ssh: cannot decode encrypted private keys")
998 Rest []byte `ssh:"rest"`
1001 if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
1005 if pk1.Check1 != pk1.Check2 {
1006 return nil, errors.New("ssh: checkint mismatch")
1009 // we only handle ed25519 and rsa keys currently
1010 switch pk1.Keytype {
1012 // https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
1021 Pad []byte `ssh:"rest"`
1024 if err := Unmarshal(pk1.Rest, &key); err != nil {
1028 for i, b := range key.Pad {
1030 return nil, errors.New("ssh: padding not as expected")
1034 pk := &rsa.PrivateKey{
1035 PublicKey: rsa.PublicKey{
1037 E: int(key.E.Int64()),
1040 Primes: []*big.Int{key.P, key.Q},
1043 if err := pk.Validate(); err != nil {
1050 case KeyAlgoED25519:
1055 Pad []byte `ssh:"rest"`
1058 if err := Unmarshal(pk1.Rest, &key); err != nil {
1062 if len(key.Priv) != ed25519.PrivateKeySize {
1063 return nil, errors.New("ssh: private key unexpected length")
1066 for i, b := range key.Pad {
1068 return nil, errors.New("ssh: padding not as expected")
1072 pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
1076 return nil, errors.New("ssh: unhandled key type")
1080 // FingerprintLegacyMD5 returns the user presentation of the key's
1081 // fingerprint as described by RFC 4716 section 4.
1082 func FingerprintLegacyMD5(pubKey PublicKey) string {
1083 md5sum := md5.Sum(pubKey.Marshal())
1084 hexarray := make([]string, len(md5sum))
1085 for i, c := range md5sum {
1086 hexarray[i] = hex.EncodeToString([]byte{c})
1088 return strings.Join(hexarray, ":")
1091 // FingerprintSHA256 returns the user presentation of the key's
1092 // fingerprint as unpadded base64 encoded sha256 hash.
1093 // This format was introduced from OpenSSH 6.8.
1094 // https://www.openssh.com/txt/release-6.8
1095 // https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding)
1096 func FingerprintSHA256(pubKey PublicKey) string {
1097 sha256sum := sha256.Sum256(pubKey.Marshal())
1098 hash := base64.RawStdEncoding.EncodeToString(sha256sum[:])
1099 return "SHA256:" + hash