2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * This class contains a RSA private key. The private key elements can
31 * be exported as arrays of bytes (minimal unsigned big-endian
34 * Private key elements are:
38 * p first modulus factor
39 * q second modulus factor
45 public class RSAPrivateKey : IPrivateKey {
95 public int KeySizeBits {
97 return ((n.Length - 1) << 3)
98 + BigInt.BitLength(n[0]);
102 public string AlgorithmName {
108 IPublicKey IPrivateKey.PublicKey {
110 return this.PublicKey;
114 public RSAPublicKey PublicKey {
116 return new RSAPublicKey(n, e);
120 byte[] n, e, d, p, q, dp, dq, iq;
123 * Create a new instance with the provided elements. Values are
124 * in unsigned big-endian representation.
129 * p first modulus factor
130 * q second modulus factor
135 * Rules verified by this constructor:
136 * n must be odd and at least 512 bits
140 * p and q are greater than 1
142 * dp must be non-zero and lower than p-1
143 * dq must be non-zero and lower than q-1
144 * iq must be non-zero and lower than p
146 * This constructor does NOT verify that:
148 * d is equal to dp modulo p-1
149 * d is equal to dq modulo q-1
150 * dp is the inverse of e modulo p-1
151 * dq is the inverse of e modulo q-1
152 * iq is the inverse of q modulo p
154 public RSAPrivateKey(byte[] n, byte[] e, byte[] d,
155 byte[] p, byte[] q, byte[] dp, byte[] dq, byte[] iq)
157 n = BigInt.NormalizeBE(n);
158 e = BigInt.NormalizeBE(e);
159 d = BigInt.NormalizeBE(d);
160 p = BigInt.NormalizeBE(p);
161 q = BigInt.NormalizeBE(q);
162 dp = BigInt.NormalizeBE(dp);
163 dq = BigInt.NormalizeBE(dq);
164 iq = BigInt.NormalizeBE(iq);
166 if (n.Length < 64 || (n.Length == 64 && n[0] < 0x80)) {
167 throw new CryptoException(
168 "Invalid RSA private key (less than 512 bits)");
170 if (!BigInt.IsOdd(n)) {
171 throw new CryptoException(
172 "Invalid RSA private key (even modulus)");
174 if (!BigInt.IsOdd(e)) {
175 throw new CryptoException(
176 "Invalid RSA private key (even exponent)");
178 if (!BigInt.IsOdd(p) || !BigInt.IsOdd(q)) {
179 throw new CryptoException(
180 "Invalid RSA private key (even factor)");
182 if (BigInt.IsOne(p) || BigInt.IsOne(q)) {
183 throw new CryptoException(
184 "Invalid RSA private key (trivial factor)");
186 if (BigInt.Compare(n, BigInt.Mul(p, q)) != 0) {
187 throw new CryptoException(
188 "Invalid RSA private key (bad factors)");
190 if (dp.Length == 0 || dq.Length == 0) {
191 throw new CryptoException(
192 "Invalid RSA private key"
193 + " (null reduced private exponent)");
197 * We can temporarily modify p[] and q[] (to compute
198 * p-1 and q-1) since these are freshly produced copies.
202 if (BigInt.Compare(dp, p) >= 0 || BigInt.Compare(dq, q) >= 0) {
203 throw new CryptoException(
204 "Invalid RSA private key"
205 + " (oversized reduced private exponent)");
209 if (iq.Length == 0 || BigInt.Compare(iq, p) >= 0) {
210 throw new CryptoException(
211 "Invalid RSA private key"
212 + " (out of range CRT coefficient)");
225 * Create a new instance with the provided elements: the two
226 * factors, and the public exponent. The other elements are
227 * computed. Values are in unsigned big-endian representation.
228 * Rules verified by this constructor:
231 * e must be relatively prime to both p-1 and q-1
232 * e must be greater than 1
233 * p*q must have size at least 512 bits
234 * TODO: not implemented yet.
236 public RSAPrivateKey(byte[] p, byte[] q, byte[] e)
238 throw new Exception("NYI");
242 * Create a new instance with the provided elements: the two
243 * factors, and the public exponent. The other elements are
244 * computed. The factors are in unsigned big-endian
245 * representation; the public exponent is a small integer.
246 * Rules verified by this constructor:
249 * e must be relatively prime to both p-1 and q-1
250 * p*q must have size at least 512 bits
251 * TODO: not implemented yet.
253 public RSAPrivateKey(byte[] p, byte[] q, uint e)
254 : this(p, q, ToBytes(e))
258 static byte[] ToBytes(uint x)
260 byte[] r = new byte[4];
261 r[0] = (byte)(x >> 24);
262 r[1] = (byte)(x >> 16);
263 r[2] = (byte)(x >> 8);
269 * CheckValid() will verify that the prime factors are indeed
270 * prime, and that all other values are correct.
272 public void CheckValid()
275 * Factors ought to be prime.
277 if (!BigInt.IsPrime(p) || !BigInt.IsPrime(q)) {
278 throw new CryptoException("Invalid RSA private key"
279 + " (non-prime factor)");
283 * FIXME: Verify that:
288 * (This is not easy with existing code because p-1 and q-1
289 * are even, but ModInt tolerates only odd moduli.)
291 CheckExp(p, d, dp, e);
292 CheckExp(q, d, dq, e);
299 ModInt x = new ModInt(p);
306 throw new CryptoException("Invalid RSA private key"
307 + " (wrong CRT coefficient)");