-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathECKeypair.cs
239 lines (227 loc) · 9.25 KB
/
ECKeypair.cs
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
namespace EPK.Crypto
{
// Copyright (c) 2015 Bastian Fredriksson <bastianf@kth.se>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
internal class ECKeypair
{
private BigInteger privateKey = BigInteger.Zero;
private ECPoint publicKey = ECPoint.INF;
private IEllipticCurve curve;
/// <summary>
/// Get the private key.
/// </summary>
public BigInteger PrivateKey
{
get { return privateKey; }
}
/// <summary>
/// Get the public key.
/// </summary>
public ECPoint PublicKey
{
get { return publicKey; }
}
/// <summary>
/// Determine if this keypair has a public key.
/// </summary>
public bool HasPublicKey
{
get { return publicKey != ECPoint.INF; }
}
/// <summary>
/// Determine if this keypair has a private key.
/// </summary>
public bool HasPrivateKey
{
get { return privateKey != BigInteger.Zero; }
}
/// <summary>
/// Create an EC keypair from an existing keypair.
/// </summary>
/// <param name="publicKey">The public key or zero.</param>
/// <param name="privateKey">The private key or zero.</param>
/// <param name="curve">The curve used to generate the keypair.</param>
public ECKeypair(ECPoint publicKey, BigInteger privateKey, IEllipticCurve curve)
{
if (curve == null)
throw new ArgumentNullException("No curve specified.");
if (publicKey != ECPoint.INF)
{
if (publicKey.X > BigInteger.Pow(2, curve.BitSize) - 1)
{
throw new ArgumentException("Bad size on public key (x-coordinate).");
}
if (publicKey.Y > BigInteger.Pow(2, curve.BitSize) - 1)
{
throw new ArgumentException("Bad size on public key (y-coordinate).");
}
if (!curve.OnCurve(publicKey))
{
throw new ArgumentException("The public key is not a valid curve point.");
}
}
if (privateKey != BigInteger.Zero && privateKey > BigInteger.Pow(2, curve.BitSize) - 1)
throw new ArgumentException("Bad size on private key.");
this.publicKey = publicKey;
this.privateKey = privateKey;
this.curve = curve;
}
/// <summary>
/// Load a keypair from a PEM file.
/// </summary>
/// <param name="file">The path to the PEM file where the keys are stored.</param>
/// <returns>A keypair or null if an error occurred.</returns>
public static ECKeypair Load(string file)
{
try
{
string line;
BigInteger privateKey = BigInteger.Zero;
ECPoint publicKey = ECPoint.INF;
IEllipticCurve curve = null;
using (var reader = new StreamReader(file))
{
while ((line = reader.ReadLine()) != null)
{
if (line == "-----BEGIN EC PARAMETERS----")
curve = ReadEllipticCurve(reader);
else if (line == "-----BEGIN EC PUBLIC KEY----")
publicKey = ReadPublicKey(reader);
else if (line == "-----BEGIN EC PUBLIC KEY----")
privateKey = ReadPrivateKey(reader);
}
}
if (privateKey == null && publicKey == null)
{
throw new FormatException("File does not contain any valid keys.");
}
return new ECKeypair(publicKey, privateKey, curve);
}
catch
{
return null;
}
}
private static IEllipticCurve ReadEllipticCurve(StreamReader reader)
{
string line = reader.ReadLine();
var parameters = new List<string>();
while (line != "-----END EC PRIVATE KEY-----")
{
if (line == null)
{
throw new FormatException("Unexpected end of file.");
}
parameters.Add(line);
line = reader.ReadLine();
}
if (parameters.Count == 1)
{
if (parameters[0] == new Secp128r1().ToString())
return new Secp128r1();
}
// Possibly support a curve specified by (P, A, B, N)?
return null;
}
private static BigInteger ReadPrivateKey(StreamReader reader)
{
string line = reader.ReadLine();
var sb = new StringBuilder();
while (line != "-----END EC PRIVATE KEY-----")
{
if (line == null)
{
throw new FormatException("Unexpected end of file.");
}
sb.Append(line);
line = reader.ReadLine();
}
return new BigInteger(Convert.FromBase64String(sb.ToString()));
}
private static ECPoint ReadPublicKey(StreamReader reader)
{
string line = reader.ReadLine();
var sb = new StringBuilder();
while (line != "-----END EC PUBLIC KEY-----")
{
if (line == null)
{
throw new FormatException("Unexpected end of file.");
}
sb.Append(line);
line = reader.ReadLine();
}
BigInteger x = new BigInteger(Convert.FromBase64String(sb.ToString().Split(new char[] { '#' })[0]));
BigInteger y = new BigInteger(Convert.FromBase64String(sb.ToString().Split(new char[] { '#' })[1]));
return new ECPoint(x, y);
}
/// <summary>
/// Store the keypair in a PEM file.
/// Description of the format used: https://goo.gl/zAtXSj
/// </summary>
/// <param name="file">The name of the keyfile, will be created or overwritten.</param>
/// <param name="savePrivateKey">True if the private key should be written, defaults to false</param>
/// <returns>True if the keypair was saved successfully.</returns>
public bool Store(string file, bool savePrivateKey = false)
{
try
{
using (var writer = new StreamWriter(file))
{
writer.WriteLine("-----BEGIN EC PARAMETERS-----");
writer.WriteLine(curve.ToString());
writer.WriteLine("-----END EC PARAMETERS-----");
if (HasPrivateKey && savePrivateKey)
{
writer.WriteLine("-----BEGIN EC PRIVATE KEY-----");
string privateKeyBase64 = Convert.ToBase64String(privateKey.ToByteArray());
foreach (string chunk in privateKeyBase64.Chunks(64))
{
writer.WriteLine(chunk);
}
writer.WriteLine("-----END EC PRIVATE KEY-----");
}
if (HasPublicKey)
{
writer.WriteLine("-----BEGIN EC PUBLIC KEY-----");
string publicKeyBase64 = Convert.ToBase64String(publicKey.X.ToByteArray()) +
"#" + Convert.ToBase64String(publicKey.Y.ToByteArray());
foreach (string chunk in publicKeyBase64.Chunks(64))
{
writer.WriteLine(chunk);
}
writer.WriteLine("-----END EC PUBLIC KEY-----");
}
}
return true;
}
catch
{
return false;
}
}
}
}