summary refs log tree commit diff
path: root/crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs
blob: 02e6928e05e2558a1f8733b50027beb7269cb016 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
using System;

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;

namespace Org.BouncyCastle.Utilities.SSH
{
    public static class OpenSshPublicKeyUtilities
    {
        private static readonly String RSA = "ssh-rsa";
        private static readonly String ECDSA = "ecdsa";
        private static readonly String ED_25519 = "ssh-ed25519";
        private static readonly String DSS = "ssh-dss";

        /**
         * Parse a public key.
         * <p/>
         * This method accepts the bytes that are Base64 encoded in an OpenSSH public key file.
         *
         * @param encoded The key.
         * @return An AsymmetricKeyParameter instance.
         */
        public static AsymmetricKeyParameter ParsePublicKey(byte[] encoded)
        {
            SshBuffer buffer = new SshBuffer(encoded);
            return ParsePublicKey(buffer);
        }

        /**
         * Encode a public key from an AsymmetricKeyParameter instance.
         *
         * @param cipherParameters The key to encode.
         * @return the key OpenSSH encoded.
         * @throws IOException
         */
        public static byte[] EncodePublicKey(AsymmetricKeyParameter cipherParameters)
        {
            if (cipherParameters == null)
            {
                throw new ArgumentException("cipherParameters was null.");
            }

            if (cipherParameters is RsaKeyParameters)
            {
                if (cipherParameters.IsPrivate)
                {
                    throw new ArgumentException("RSAKeyParamaters was for encryption");
                }

                RsaKeyParameters rsaPubKey = (RsaKeyParameters)cipherParameters;

                SshBuilder builder = new SshBuilder();
                builder.WriteString(RSA);
                builder.WriteBigNum(rsaPubKey.Exponent);
                builder.WriteBigNum(rsaPubKey.Modulus);

                return builder.GetBytes();

            }
            else if (cipherParameters is ECPublicKeyParameters ecPublicKey)
            {
                SshBuilder builder = new SshBuilder();

                //
                // checked for named curve parameters..
                //
                String name = SshNamedCurves.GetNameForParameters(ecPublicKey.Parameters);

                if (name == null)
                {
                    throw new ArgumentException("unable to derive ssh curve name for " + ecPublicKey.Parameters.Curve.GetType().Name);
                }

                builder.WriteString(ECDSA + "-sha2-" + name); // Magic
                builder.WriteString(name);
                builder.WriteBlock(ecPublicKey.Q.GetEncoded(false)); //Uncompressed
                return builder.GetBytes();
            }
            else if (cipherParameters is DsaPublicKeyParameters dsaPubKey)
            {
                DsaParameters dsaParams = dsaPubKey.Parameters;

                SshBuilder builder = new SshBuilder();
                builder.WriteString(DSS);
                builder.WriteBigNum(dsaParams.P);
                builder.WriteBigNum(dsaParams.Q);
                builder.WriteBigNum(dsaParams.G);
                builder.WriteBigNum(dsaPubKey.Y);
                return builder.GetBytes();
            }
            else if (cipherParameters is Ed25519PublicKeyParameters ed25519PublicKey)
            {
                SshBuilder builder = new SshBuilder();
                builder.WriteString(ED_25519);
                builder.WriteBlock(ed25519PublicKey.GetEncoded());
                return builder.GetBytes();
            }

            throw new ArgumentException("unable to convert " + cipherParameters.GetType().Name + " to private key");
        }

        /**
         * Parse a public key from an SSHBuffer instance.
         *
         * @param buffer containing the SSH public key.
         * @return A CipherParameters instance.
         */
        private static AsymmetricKeyParameter ParsePublicKey(SshBuffer buffer)
        {
            AsymmetricKeyParameter result = null;

            string magic = buffer.ReadString();
            if (RSA.Equals(magic))
            {
                BigInteger e = buffer.ReadBigNumPositive();
                BigInteger n = buffer.ReadBigNumPositive();
                result = new RsaKeyParameters(false, n, e);
            }
            else if (DSS.Equals(magic))
            {
                BigInteger p = buffer.ReadBigNumPositive();
                BigInteger q = buffer.ReadBigNumPositive();
                BigInteger g = buffer.ReadBigNumPositive();
                BigInteger pubKey = buffer.ReadBigNumPositive();

                result = new DsaPublicKeyParameters(pubKey, new DsaParameters(p, q, g));
            }
            else if (magic.StartsWith(ECDSA))
            {
                String curveName = buffer.ReadString();
                DerObjectIdentifier oid = SshNamedCurves.GetByName(curveName);
                X9ECParameters x9ECParameters = SshNamedCurves.GetParameters(oid) ?? 
                    throw new InvalidOperationException("unable to find curve for " + magic + " using curve name " + curveName);
                var curve = x9ECParameters.Curve;
                byte[] pointRaw = buffer.ReadBlock();

                result = new ECPublicKeyParameters(
                    curve.DecodePoint(pointRaw),
                    new ECNamedDomainParameters(oid, x9ECParameters));
            }
            else if (ED_25519.Equals(magic))
            {
                byte[] pubKeyBytes = buffer.ReadBlock();
                if (pubKeyBytes.Length != Ed25519PublicKeyParameters.KeySize)
                {
                    throw new InvalidOperationException("public key value of wrong length");
                }

                result = new Ed25519PublicKeyParameters(pubKeyBytes, 0);
            }

            if (result == null)
            {
                throw new ArgumentException("unable to parse key");
            }

            if (buffer.HasRemaining())
            {
                throw new ArgumentException("decoded key has trailing data");
            }

            return result;
        }
    }
}