openssh.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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.
  4. // Code originally from https://go-review.googlesource.com/c/crypto/+/218620
  5. // TODO: replace with upstream once the above change is merged and released.
  6. package format
  7. import (
  8. "crypto"
  9. "crypto/ecdsa"
  10. "crypto/ed25519"
  11. "crypto/elliptic"
  12. "crypto/rand"
  13. "crypto/rsa"
  14. "encoding/binary"
  15. "encoding/pem"
  16. "fmt"
  17. "math/big"
  18. "golang.org/x/crypto/ssh"
  19. )
  20. const privateKeyAuthMagic = "openssh-key-v1\x00"
  21. type openSSHEncryptedPrivateKey struct {
  22. CipherName string
  23. KDFName string
  24. KDFOptions string
  25. KeysCount uint32
  26. PubKey []byte
  27. KeyBlocks []byte
  28. }
  29. type openSSHPrivateKey struct {
  30. Check1 uint32
  31. Check2 uint32
  32. Keytype string
  33. Rest []byte `ssh:"rest"`
  34. }
  35. type openSSHRSAPrivateKey struct {
  36. N *big.Int
  37. E *big.Int
  38. D *big.Int
  39. Iqmp *big.Int
  40. P *big.Int
  41. Q *big.Int
  42. Comment string
  43. Pad []byte `ssh:"rest"`
  44. }
  45. type openSSHECDSAPrivateKey struct {
  46. Curve string
  47. Pub []byte
  48. D *big.Int
  49. Comment string
  50. Pad []byte `ssh:"rest"`
  51. }
  52. type openSSHEd25519PrivateKey struct {
  53. Pub []byte
  54. Priv []byte
  55. Comment string
  56. Pad []byte `ssh:"rest"`
  57. }
  58. func OpenSSHPrivateKey(key crypto.PrivateKey, comment string) (*pem.Block, error) {
  59. var check uint32
  60. if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil {
  61. return nil, err
  62. }
  63. var pk1 openSSHPrivateKey
  64. pk1.Check1 = check
  65. pk1.Check2 = check
  66. var w openSSHEncryptedPrivateKey
  67. w.KeysCount = 1
  68. if k, ok := key.(*ed25519.PrivateKey); ok {
  69. key = *k
  70. }
  71. switch k := key.(type) {
  72. case *rsa.PrivateKey:
  73. e := new(big.Int).SetInt64(int64(k.E))
  74. key := openSSHRSAPrivateKey{
  75. N: k.N,
  76. E: e,
  77. D: k.D,
  78. Iqmp: k.Precomputed.Qinv,
  79. P: k.Primes[0],
  80. Q: k.Primes[1],
  81. Comment: comment,
  82. }
  83. pk1.Keytype = ssh.KeyAlgoRSA
  84. pk1.Rest = ssh.Marshal(key)
  85. w.PubKey = ssh.Marshal(struct {
  86. KeyType string
  87. E *big.Int
  88. N *big.Int
  89. }{
  90. ssh.KeyAlgoRSA, e, k.N,
  91. })
  92. case *ecdsa.PrivateKey:
  93. var curve, keytype string
  94. switch name := k.Curve.Params().Name; name {
  95. case "P-256":
  96. curve = "nistp256"
  97. keytype = ssh.KeyAlgoECDSA256
  98. case "P-384":
  99. curve = "nistp384"
  100. keytype = ssh.KeyAlgoECDSA384
  101. case "P-521":
  102. curve = "nistp521"
  103. keytype = ssh.KeyAlgoECDSA521
  104. default:
  105. return nil, fmt.Errorf("ssh: unknown curve %q", name)
  106. }
  107. pub := elliptic.Marshal(k.Curve, k.X, k.Y)
  108. key := openSSHECDSAPrivateKey{
  109. Curve: curve,
  110. Pub: pub,
  111. D: k.D,
  112. Comment: comment,
  113. }
  114. pk1.Keytype = keytype
  115. pk1.Rest = ssh.Marshal(key)
  116. w.PubKey = ssh.Marshal(struct {
  117. KeyType string
  118. Curve string
  119. Pub []byte
  120. }{
  121. keytype, curve, pub,
  122. })
  123. case ed25519.PrivateKey:
  124. pub, priv := k[32:], k
  125. key := openSSHEd25519PrivateKey{
  126. Pub: pub,
  127. Priv: priv,
  128. Comment: comment,
  129. }
  130. pk1.Keytype = ssh.KeyAlgoED25519
  131. pk1.Rest = ssh.Marshal(key)
  132. w.PubKey = ssh.Marshal(struct {
  133. KeyType string
  134. Pub []byte
  135. }{
  136. ssh.KeyAlgoED25519, pub,
  137. })
  138. default:
  139. return nil, fmt.Errorf("ssh: unknown key type %T", k)
  140. }
  141. w.KeyBlocks = openSSHPadding(ssh.Marshal(pk1), 8)
  142. w.CipherName, w.KDFName, w.KDFOptions = "none", "none", ""
  143. return &pem.Block{
  144. Type: "OPENSSH PRIVATE KEY",
  145. Bytes: append([]byte(privateKeyAuthMagic), ssh.Marshal(w)...),
  146. }, nil
  147. }
  148. func openSSHPadding(block []byte, blocksize int) []byte {
  149. for i, j := 0, len(block); (j+i)%blocksize != 0; i++ {
  150. block = append(block, byte(i+1))
  151. }
  152. return block
  153. }