File: //var/opt/nydus/ops/oscrypto/_win/asymmetric.py
# coding: utf-8
from __future__ import unicode_literals, division, absolute_import, print_function
import os
import sys
import hashlib
import random
from .._asn1 import (
Certificate as Asn1Certificate,
DHParameters,
DSAParams,
DSASignature,
ECDomainParameters,
ECPrivateKey,
Integer,
int_from_bytes,
int_to_bytes,
PrivateKeyAlgorithm,
PrivateKeyInfo,
PublicKeyAlgorithm,
PublicKeyInfo,
RSAPrivateKey,
RSAPublicKey,
)
from .._asymmetric import (
_CertificateBase,
_fingerprint,
_parse_pkcs12,
_PrivateKeyBase,
_PublicKeyBase,
_unwrap_private_key_info,
parse_certificate,
parse_private,
parse_public,
)
from .._errors import pretty_message
from .._ffi import (
buffer_from_bytes,
buffer_from_unicode,
byte_array,
bytes_from_buffer,
cast,
deref,
native,
new,
null,
pointer_set,
sizeof,
struct,
struct_bytes,
struct_from_buffer,
unwrap,
write_to_buffer,
)
from .. import backend
from .._int import fill_width
from ..errors import AsymmetricKeyError, IncompleteAsymmetricKeyError, SignatureError
from .._types import type_name, str_cls, byte_cls, int_types
from .._pkcs1 import (
add_pkcs1v15_signature_padding,
add_pss_padding,
raw_rsa_private_crypt,
raw_rsa_public_crypt,
remove_pkcs1v15_signature_padding,
verify_pss_padding,
)
from ..util import constant_compare
_gwv = sys.getwindowsversion()
_win_version_info = (_gwv[0], _gwv[1])
_backend = backend()
if _backend == 'winlegacy':
from ._advapi32 import advapi32, Advapi32Const, handle_error, open_context_handle, close_context_handle
from .._ecdsa import (
ec_generate_pair as _pure_python_ec_generate_pair,
ec_compute_public_key_point as _pure_python_ec_compute_public_key_point,
ec_public_key_info,
ecdsa_sign as _pure_python_ecdsa_sign,
ecdsa_verify as _pure_python_ecdsa_verify,
)
else:
from ._cng import bcrypt, BcryptConst, handle_error, open_alg_handle, close_alg_handle
__all__ = [
'Certificate',
'dsa_sign',
'dsa_verify',
'ecdsa_sign',
'ecdsa_verify',
'generate_pair',
'load_certificate',
'load_pkcs12',
'load_private_key',
'load_public_key',
'parse_pkcs12',
'PrivateKey',
'PublicKey',
'rsa_oaep_decrypt',
'rsa_oaep_encrypt',
'rsa_pkcs1v15_decrypt',
'rsa_pkcs1v15_encrypt',
'rsa_pkcs1v15_sign',
'rsa_pkcs1v15_verify',
'rsa_pss_sign',
'rsa_pss_verify',
]
# A list of primes from OpenSSL's bn_prime.h to use when testing primality of a
# large integer
_SMALL_PRIMES = [
2, 3, 5, 7, 11, 13, 17, 19,
23, 29, 31, 37, 41, 43, 47, 53,
59, 61, 67, 71, 73, 79, 83, 89,
97, 101, 103, 107, 109, 113, 127, 131,
137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223,
227, 229, 233, 239, 241, 251, 257, 263,
269, 271, 277, 281, 283, 293, 307, 311,
313, 317, 331, 337, 347, 349, 353, 359,
367, 373, 379, 383, 389, 397, 401, 409,
419, 421, 431, 433, 439, 443, 449, 457,
461, 463, 467, 479, 487, 491, 499, 503,
509, 521, 523, 541, 547, 557, 563, 569,
571, 577, 587, 593, 599, 601, 607, 613,
617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719,
727, 733, 739, 743, 751, 757, 761, 769,
773, 787, 797, 809, 811, 821, 823, 827,
829, 839, 853, 857, 859, 863, 877, 881,
883, 887, 907, 911, 919, 929, 937, 941,
947, 953, 967, 971, 977, 983, 991, 997,
1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049,
1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097,
1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163,
1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283,
1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321,
1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423,
1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459,
1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571,
1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619,
1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693,
1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747,
1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877,
1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949,
1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003,
2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069,
2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129,
2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203,
2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267,
2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311,
2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377,
2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423,
2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503,
2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579,
2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657,
2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693,
2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741,
2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801,
2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861,
2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939,
2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011,
3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079,
3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167,
3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221,
3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301,
3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347,
3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413,
3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491,
3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541,
3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607,
3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671,
3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727,
3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797,
3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863,
3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923,
3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003,
4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057,
4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129,
4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211,
4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259,
4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337,
4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481,
4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547,
4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621,
4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673,
4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813,
4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909,
4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967,
4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011,
5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167,
5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233,
5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309,
5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399,
5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507,
5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573,
5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653,
5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711,
5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849,
5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897,
5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007,
6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073,
6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211,
6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271,
6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329,
6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379,
6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563,
6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637,
6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701,
6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779,
6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907,
6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971,
6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027,
7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121,
7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253,
7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349,
7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457,
7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517,
7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621,
7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691,
7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757,
7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853,
7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009,
8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087,
8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161,
8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231,
8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291,
8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369,
8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443,
8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537,
8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609,
8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731,
8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803,
8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861,
8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941,
8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091,
9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161,
9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227,
9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311,
9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377,
9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433,
9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491,
9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587,
9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649,
9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791,
9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857,
9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929,
9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037,
10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099,
10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163,
10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247,
10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303,
10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369,
10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459,
10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531,
10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627,
10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691,
10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771,
10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859,
10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937,
10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003,
11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087,
11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161,
11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251,
11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317,
11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399,
11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483,
11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551,
11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657,
11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731,
11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813,
11821, 11827, 11831, 11833, 11839, 11863, 11867, 11887,
11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941,
11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011,
12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101,
12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161,
12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251,
12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323,
12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401,
12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473,
12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527,
12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589,
12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653,
12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739,
12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821,
12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907,
12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967,
12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033,
13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109,
13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177,
13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259,
13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337,
13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421,
13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499,
13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597,
13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681,
13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723,
13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799,
13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879,
13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933,
13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033,
14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143,
14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221,
14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323,
14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407,
14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461,
14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549,
14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627,
14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699,
14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753,
14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821,
14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887,
14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957,
14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073,
15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137,
15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217,
15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277,
15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331,
15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401,
15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473,
15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569,
15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643,
15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727,
15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773,
15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859,
15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919,
15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007,
16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087,
16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183,
16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249,
16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349,
16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427,
16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493,
16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603,
16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661,
16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747,
16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843,
16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927,
16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993,
17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053,
17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159,
17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231,
17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327,
17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389,
17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467,
17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519,
17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599,
17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683,
17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783,
17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863,
]
class _WinKey():
# A CNG BCRYPT_KEY_HANDLE on Vista and newer, an HCRYPTKEY on XP and 2003
key_handle = None
# On XP and 2003, we have to carry around more info
context_handle = None
ex_key_handle = None
# A reference to the library used in the destructor to make sure it hasn't
# been garbage collected by the time this object is garbage collected
_lib = None
def __init__(self, key_handle, asn1):
"""
:param key_handle:
A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY
(XP and 2003) from loading/importing the key
:param asn1:
An asn1crypto object for the concrete type
"""
self.key_handle = key_handle
self.asn1 = asn1
if _backend == 'winlegacy':
self._lib = advapi32
else:
self._lib = bcrypt
def __del__(self):
if self.key_handle:
if _backend == 'winlegacy':
res = self._lib.CryptDestroyKey(self.key_handle)
else:
res = self._lib.BCryptDestroyKey(self.key_handle)
handle_error(res)
self.key_handle = None
if self.context_handle and _backend == 'winlegacy':
close_context_handle(self.context_handle)
self.context_handle = None
self._lib = None
class PrivateKey(_WinKey, _PrivateKeyBase):
"""
Container for the OS crypto library representation of a private key
"""
_public_key = None
def __init__(self, key_handle, asn1):
"""
:param key_handle:
A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY
(XP and 2003) from loading/importing the key
:param asn1:
An asn1crypto.keys.PrivateKeyInfo object
"""
_WinKey.__init__(self, key_handle, asn1)
@property
def public_key(self):
"""
:return:
A PublicKey object corresponding to this private key.
"""
if _backend == 'winlegacy':
if self.algorithm == 'ec':
pub_point = _pure_python_ec_compute_public_key_point(self.asn1)
self._public_key = PublicKey(None, ec_public_key_info(pub_point, self.curve))
elif self.algorithm == 'dsa':
# The DSA provider won't allow exporting the private key with
# CryptoImportKey flags set to 0 and won't allow flags to be set
# to CRYPT_EXPORTABLE, so we manually recreated the public key
# ASN.1
params = self.asn1['private_key_algorithm']['parameters']
pub_asn1 = PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'dsa',
'parameters': params
}),
'public_key': Integer(pow(
params['g'].native,
self.asn1['private_key'].parsed.native,
params['p'].native
))
})
self._public_key = load_public_key(pub_asn1)
else:
# This suffers from similar problems as above, although not
# as insurmountable. This is just a simpler/faster solution
# since the private key has all of the data we need anyway
parsed = self.asn1['private_key'].parsed
pub_asn1 = PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'rsa'
}),
'public_key': RSAPublicKey({
'modulus': parsed['modulus'],
'public_exponent': parsed['public_exponent']
})
})
self._public_key = load_public_key(pub_asn1)
else:
pub_asn1, _ = _bcrypt_key_handle_to_asn1(self.algorithm, self.bit_size, self.key_handle)
self._public_key = load_public_key(pub_asn1)
return self._public_key
@property
def fingerprint(self):
"""
Creates a fingerprint that can be compared with a public key to see if
the two form a pair.
This fingerprint is not compatible with fingerprints generated by any
other software.
:return:
A byte string that is a sha256 hash of selected components (based
on the key type)
"""
if self._fingerprint is None:
self._fingerprint = _fingerprint(self.asn1, load_private_key)
return self._fingerprint
class PublicKey(_WinKey, _PublicKeyBase):
"""
Container for the OS crypto library representation of a public key
"""
def __init__(self, key_handle, asn1):
"""
:param key_handle:
A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY
(XP and 2003) from loading/importing the key
:param asn1:
An asn1crypto.keys.PublicKeyInfo object
"""
_WinKey.__init__(self, key_handle, asn1)
class Certificate(_WinKey, _CertificateBase):
"""
Container for the OS crypto library representation of a certificate
"""
_public_key = None
_self_signed = None
def __init__(self, key_handle, asn1):
"""
:param key_handle:
A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY
(XP and 2003) from loading/importing the certificate
:param asn1:
An asn1crypto.x509.Certificate object
"""
_WinKey.__init__(self, key_handle, asn1)
@property
def public_key(self):
"""
:return:
The PublicKey object for the public key this certificate contains
"""
if self._public_key is None:
self._public_key = load_public_key(self.asn1['tbs_certificate']['subject_public_key_info'])
return self._public_key
@property
def self_signed(self):
"""
:return:
A boolean - if the certificate is self-signed
"""
if self._self_signed is None:
self._self_signed = False
if self.asn1.self_signed in set(['yes', 'maybe']):
signature_algo = self.asn1['signature_algorithm'].signature_algo
hash_algo = self.asn1['signature_algorithm'].hash_algo
if signature_algo == 'rsassa_pkcs1v15':
verify_func = rsa_pkcs1v15_verify
elif signature_algo == 'rsassa_pss':
verify_func = rsa_pss_verify
elif signature_algo == 'dsa':
verify_func = dsa_verify
elif signature_algo == 'ecdsa':
verify_func = ecdsa_verify
else:
raise OSError(pretty_message(
'''
Unable to verify the signature of the certificate since
it uses the unsupported algorithm %s
''',
signature_algo
))
try:
verify_func(
self,
self.asn1['signature_value'].native,
self.asn1['tbs_certificate'].dump(),
hash_algo
)
self._self_signed = True
except (SignatureError):
pass
return self._self_signed
def generate_pair(algorithm, bit_size=None, curve=None):
"""
Generates a public/private key pair
:param algorithm:
The key algorithm - "rsa", "dsa" or "ec"
:param bit_size:
An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024,
2048, 3072 or 4096. For "dsa" the value may be 1024, plus 2048 or 3072
if on Windows 8 or newer.
:param curve:
A unicode string - used for "ec" keys. Valid values include "secp256r1",
"secp384r1" and "secp521r1".
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A 2-element tuple of (PublicKey, PrivateKey). The contents of each key
may be saved by calling .asn1.dump().
"""
if algorithm not in set(['rsa', 'dsa', 'ec']):
raise ValueError(pretty_message(
'''
algorithm must be one of "rsa", "dsa", "ec", not %s
''',
repr(algorithm)
))
if algorithm == 'rsa':
if bit_size not in set([1024, 2048, 3072, 4096]):
raise ValueError(pretty_message(
'''
bit_size must be one of 1024, 2048, 3072, 4096, not %s
''',
repr(bit_size)
))
elif algorithm == 'dsa':
# Windows Vista and 7 only support SHA1-based DSA keys
if _win_version_info < (6, 2) or _backend == 'winlegacy':
if bit_size != 1024:
raise ValueError(pretty_message(
'''
bit_size must be 1024, not %s
''',
repr(bit_size)
))
else:
if bit_size not in set([1024, 2048, 3072]):
raise ValueError(pretty_message(
'''
bit_size must be one of 1024, 2048, 3072, not %s
''',
repr(bit_size)
))
elif algorithm == 'ec':
if curve not in set(['secp256r1', 'secp384r1', 'secp521r1']):
raise ValueError(pretty_message(
'''
curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s
''',
repr(curve)
))
if _backend == 'winlegacy':
if algorithm == 'ec':
pub_info, priv_info = _pure_python_ec_generate_pair(curve)
return (PublicKey(None, pub_info), PrivateKey(None, priv_info))
return _advapi32_generate_pair(algorithm, bit_size)
else:
return _bcrypt_generate_pair(algorithm, bit_size, curve)
def _advapi32_key_handle_to_asn1(algorithm, bit_size, key_handle):
"""
Accepts an key handle and exports it to ASN.1
:param algorithm:
The key algorithm - "rsa" or "dsa"
:param bit_size:
An integer - only used when algorithm is "rsa"
:param key_handle:
The handle to export
:return:
A 2-element tuple of asn1crypto.keys.PrivateKeyInfo and
asn1crypto.keys.PublicKeyInfo
"""
if algorithm == 'rsa':
struct_type = 'RSABLOBHEADER'
else:
struct_type = 'DSSBLOBHEADER'
out_len = new(advapi32, 'DWORD *')
res = advapi32.CryptExportKey(
key_handle,
null(),
Advapi32Const.PRIVATEKEYBLOB,
0,
null(),
out_len
)
handle_error(res)
buffer_length = deref(out_len)
buffer_ = buffer_from_bytes(buffer_length)
res = advapi32.CryptExportKey(
key_handle,
null(),
Advapi32Const.PRIVATEKEYBLOB,
0,
buffer_,
out_len
)
handle_error(res)
blob_struct_pointer = struct_from_buffer(advapi32, struct_type, buffer_)
blob_struct = unwrap(blob_struct_pointer)
struct_size = sizeof(advapi32, blob_struct)
private_blob = bytes_from_buffer(buffer_, buffer_length)[struct_size:]
if algorithm == 'rsa':
public_info, private_info = _advapi32_interpret_rsa_key_blob(bit_size, blob_struct, private_blob)
else:
# The public key for a DSA key is not available in from the private
# key blob, so we have to separately export the public key
public_out_len = new(advapi32, 'DWORD *')
res = advapi32.CryptExportKey(
key_handle,
null(),
Advapi32Const.PUBLICKEYBLOB,
0,
null(),
public_out_len
)
handle_error(res)
public_buffer_length = deref(public_out_len)
public_buffer = buffer_from_bytes(public_buffer_length)
res = advapi32.CryptExportKey(
key_handle,
null(),
Advapi32Const.PUBLICKEYBLOB,
0,
public_buffer,
public_out_len
)
handle_error(res)
public_blob = bytes_from_buffer(public_buffer, public_buffer_length)[struct_size:]
public_info, private_info = _advapi32_interpret_dsa_key_blob(bit_size, public_blob, private_blob)
return (public_info, private_info)
def _advapi32_generate_pair(algorithm, bit_size=None):
"""
Generates a public/private key pair using CryptoAPI
:param algorithm:
The key algorithm - "rsa" or "dsa"
:param bit_size:
An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024,
2048, 3072 or 4096. For "dsa" the value may be 1024.
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A 2-element tuple of (PublicKey, PrivateKey). The contents of each key
may be saved by calling .asn1.dump().
"""
if algorithm == 'rsa':
provider = Advapi32Const.MS_ENH_RSA_AES_PROV
algorithm_id = Advapi32Const.CALG_RSA_SIGN
else:
provider = Advapi32Const.MS_ENH_DSS_DH_PROV
algorithm_id = Advapi32Const.CALG_DSS_SIGN
context_handle = None
key_handle = None
try:
context_handle = open_context_handle(provider, verify_only=False)
key_handle_pointer = new(advapi32, 'HCRYPTKEY *')
flags = (bit_size << 16) | Advapi32Const.CRYPT_EXPORTABLE
res = advapi32.CryptGenKey(context_handle, algorithm_id, flags, key_handle_pointer)
handle_error(res)
key_handle = unwrap(key_handle_pointer)
public_info, private_info = _advapi32_key_handle_to_asn1(algorithm, bit_size, key_handle)
return (load_public_key(public_info), load_private_key(private_info))
finally:
if context_handle:
close_context_handle(context_handle)
if key_handle:
advapi32.CryptDestroyKey(key_handle)
def _bcrypt_key_handle_to_asn1(algorithm, bit_size, key_handle):
"""
Accepts an key handle and exports it to ASN.1
:param algorithm:
The key algorithm - "rsa", "dsa" or "ec"
:param bit_size:
An integer - only used when algorithm is "dsa"
:param key_handle:
The handle to export
:return:
A 2-element tuple of asn1crypto.keys.PrivateKeyInfo and
asn1crypto.keys.PublicKeyInfo
"""
if algorithm == 'rsa':
struct_type = 'BCRYPT_RSAKEY_BLOB'
private_blob_type = BcryptConst.BCRYPT_RSAFULLPRIVATE_BLOB
public_blob_type = BcryptConst.BCRYPT_RSAPUBLIC_BLOB
elif algorithm == 'dsa':
if bit_size > 1024:
struct_type = 'BCRYPT_DSA_KEY_BLOB_V2'
else:
struct_type = 'BCRYPT_DSA_KEY_BLOB'
private_blob_type = BcryptConst.BCRYPT_DSA_PRIVATE_BLOB
public_blob_type = BcryptConst.BCRYPT_DSA_PUBLIC_BLOB
else:
struct_type = 'BCRYPT_ECCKEY_BLOB'
private_blob_type = BcryptConst.BCRYPT_ECCPRIVATE_BLOB
public_blob_type = BcryptConst.BCRYPT_ECCPUBLIC_BLOB
private_out_len = new(bcrypt, 'ULONG *')
res = bcrypt.BCryptExportKey(key_handle, null(), private_blob_type, null(), 0, private_out_len, 0)
handle_error(res)
private_buffer_length = deref(private_out_len)
private_buffer = buffer_from_bytes(private_buffer_length)
res = bcrypt.BCryptExportKey(
key_handle,
null(),
private_blob_type,
private_buffer,
private_buffer_length,
private_out_len,
0
)
handle_error(res)
private_blob_struct_pointer = struct_from_buffer(bcrypt, struct_type, private_buffer)
private_blob_struct = unwrap(private_blob_struct_pointer)
struct_size = sizeof(bcrypt, private_blob_struct)
private_blob = bytes_from_buffer(private_buffer, private_buffer_length)[struct_size:]
if algorithm == 'rsa':
private_key = _bcrypt_interpret_rsa_key_blob('private', private_blob_struct, private_blob)
elif algorithm == 'dsa':
if bit_size > 1024:
private_key = _bcrypt_interpret_dsa_key_blob('private', 2, private_blob_struct, private_blob)
else:
private_key = _bcrypt_interpret_dsa_key_blob('private', 1, private_blob_struct, private_blob)
else:
private_key = _bcrypt_interpret_ec_key_blob('private', private_blob_struct, private_blob)
public_out_len = new(bcrypt, 'ULONG *')
res = bcrypt.BCryptExportKey(key_handle, null(), public_blob_type, null(), 0, public_out_len, 0)
handle_error(res)
public_buffer_length = deref(public_out_len)
public_buffer = buffer_from_bytes(public_buffer_length)
res = bcrypt.BCryptExportKey(
key_handle,
null(),
public_blob_type,
public_buffer,
public_buffer_length,
public_out_len,
0
)
handle_error(res)
public_blob_struct_pointer = struct_from_buffer(bcrypt, struct_type, public_buffer)
public_blob_struct = unwrap(public_blob_struct_pointer)
struct_size = sizeof(bcrypt, public_blob_struct)
public_blob = bytes_from_buffer(public_buffer, public_buffer_length)[struct_size:]
if algorithm == 'rsa':
public_key = _bcrypt_interpret_rsa_key_blob('public', public_blob_struct, public_blob)
elif algorithm == 'dsa':
if bit_size > 1024:
public_key = _bcrypt_interpret_dsa_key_blob('public', 2, public_blob_struct, public_blob)
else:
public_key = _bcrypt_interpret_dsa_key_blob('public', 1, public_blob_struct, public_blob)
else:
public_key = _bcrypt_interpret_ec_key_blob('public', public_blob_struct, public_blob)
return (public_key, private_key)
def _bcrypt_generate_pair(algorithm, bit_size=None, curve=None):
"""
Generates a public/private key pair using CNG
:param algorithm:
The key algorithm - "rsa", "dsa" or "ec"
:param bit_size:
An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024,
2048, 3072 or 4096. For "dsa" the value may be 1024, plus 2048 or 3072
if on Windows 8 or newer.
:param curve:
A unicode string - used for "ec" keys. Valid values include "secp256r1",
"secp384r1" and "secp521r1".
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A 2-element tuple of (PublicKey, PrivateKey). The contents of each key
may be saved by calling .asn1.dump().
"""
if algorithm == 'rsa':
alg_constant = BcryptConst.BCRYPT_RSA_ALGORITHM
elif algorithm == 'dsa':
alg_constant = BcryptConst.BCRYPT_DSA_ALGORITHM
else:
alg_constant = {
'secp256r1': BcryptConst.BCRYPT_ECDSA_P256_ALGORITHM,
'secp384r1': BcryptConst.BCRYPT_ECDSA_P384_ALGORITHM,
'secp521r1': BcryptConst.BCRYPT_ECDSA_P521_ALGORITHM,
}[curve]
bit_size = {
'secp256r1': 256,
'secp384r1': 384,
'secp521r1': 521,
}[curve]
key_handle = None
try:
alg_handle = open_alg_handle(alg_constant)
key_handle_pointer = new(bcrypt, 'BCRYPT_KEY_HANDLE *')
res = bcrypt.BCryptGenerateKeyPair(alg_handle, key_handle_pointer, bit_size, 0)
handle_error(res)
key_handle = unwrap(key_handle_pointer)
res = bcrypt.BCryptFinalizeKeyPair(key_handle, 0)
handle_error(res)
public_key, private_key = _bcrypt_key_handle_to_asn1(algorithm, bit_size, key_handle)
finally:
if key_handle:
bcrypt.BCryptDestroyKey(key_handle)
return (load_public_key(public_key), load_private_key(private_key))
def generate_dh_parameters(bit_size):
"""
Generates DH parameters for use with Diffie-Hellman key exchange. Returns
a structure in the format of DHParameter defined in PKCS#3, which is also
used by the OpenSSL dhparam tool.
THIS CAN BE VERY TIME CONSUMING!
:param bit_size:
The integer bit size of the parameters to generate. Must be between 512
and 4096, and divisible by 64. Recommended secure value as of early 2016
is 2048, with an absolute minimum of 1024.
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
An asn1crypto.algos.DHParameters object. Use
oscrypto.asymmetric.dump_dh_parameters() to save to disk for usage with
web servers.
"""
if not isinstance(bit_size, int_types):
raise TypeError(pretty_message(
'''
bit_size must be an integer, not %s
''',
type_name(bit_size)
))
if bit_size < 512:
raise ValueError('bit_size must be greater than or equal to 512')
if bit_size > 4096:
raise ValueError('bit_size must be less than or equal to 4096')
if bit_size % 64 != 0:
raise ValueError('bit_size must be a multiple of 64')
alg_handle = None
# The following algorithm has elements taken from OpenSSL. In short, it
# generates random numbers and then ensures that they are valid for the
# hardcoded generator of 2, and then ensures the number is a "safe" prime
# by ensuring p//2 is prime also.
# OpenSSL allows use of generator 2 or 5, but we hardcode 2 since it is
# the default, and what is used by Security.framework on OS X also.
g = 2
try:
byte_size = bit_size // 8
if _backend == 'win':
alg_handle = open_alg_handle(BcryptConst.BCRYPT_RNG_ALGORITHM)
buffer = buffer_from_bytes(byte_size)
while True:
if _backend == 'winlegacy':
rb = os.urandom(byte_size)
else:
res = bcrypt.BCryptGenRandom(alg_handle, buffer, byte_size, 0)
handle_error(res)
rb = bytes_from_buffer(buffer)
p = int_from_bytes(rb)
# If a number is even, it can't be prime
if p % 2 == 0:
continue
# Perform the generator checks outlined in OpenSSL's
# dh_builtin_genparams() located in dh_gen.c
if g == 2:
if p % 24 != 11:
continue
elif g == 5:
rem = p % 10
if rem != 3 and rem != 7:
continue
divisible = False
for prime in _SMALL_PRIMES:
if p % prime == 0:
divisible = True
break
# If the number is not divisible by any of the small primes, then
# move on to the full Miller-Rabin test.
if not divisible and _is_prime(bit_size, p):
q = p // 2
if _is_prime(bit_size, q):
return DHParameters({'p': p, 'g': g})
finally:
if alg_handle:
close_alg_handle(alg_handle)
def _is_prime(bit_size, n):
"""
An implementation of Miller–Rabin for checking if a number is prime.
:param bit_size:
An integer of the number of bits in the prime number
:param n:
An integer, the prime number
:return:
A boolean
"""
r = 0
s = n - 1
while s % 2 == 0:
r += 1
s //= 2
if bit_size >= 1300:
k = 2
elif bit_size >= 850:
k = 3
elif bit_size >= 650:
k = 4
elif bit_size >= 550:
k = 5
elif bit_size >= 450:
k = 6
for _ in range(k):
a = random.randrange(2, n - 1)
x = pow(a, s, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
def _advapi32_interpret_rsa_key_blob(bit_size, blob_struct, blob):
"""
Takes a CryptoAPI RSA private key blob and converts it into the ASN.1
structures for the public and private keys
:param bit_size:
The integer bit size of the key
:param blob_struct:
An instance of the advapi32.RSAPUBKEY struct
:param blob:
A byte string of the binary data after the header
:return:
A 2-element tuple of (asn1crypto.keys.PublicKeyInfo,
asn1crypto.keys.PrivateKeyInfo)
"""
len1 = bit_size // 8
len2 = bit_size // 16
prime1_offset = len1
prime2_offset = prime1_offset + len2
exponent1_offset = prime2_offset + len2
exponent2_offset = exponent1_offset + len2
coefficient_offset = exponent2_offset + len2
private_exponent_offset = coefficient_offset + len2
public_exponent = blob_struct.rsapubkey.pubexp
modulus = int_from_bytes(blob[0:prime1_offset][::-1])
prime1 = int_from_bytes(blob[prime1_offset:prime2_offset][::-1])
prime2 = int_from_bytes(blob[prime2_offset:exponent1_offset][::-1])
exponent1 = int_from_bytes(blob[exponent1_offset:exponent2_offset][::-1])
exponent2 = int_from_bytes(blob[exponent2_offset:coefficient_offset][::-1])
coefficient = int_from_bytes(blob[coefficient_offset:private_exponent_offset][::-1])
private_exponent = int_from_bytes(blob[private_exponent_offset:private_exponent_offset + len1][::-1])
public_key_info = PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'rsa',
}),
'public_key': RSAPublicKey({
'modulus': modulus,
'public_exponent': public_exponent,
}),
})
rsa_private_key = RSAPrivateKey({
'version': 'two-prime',
'modulus': modulus,
'public_exponent': public_exponent,
'private_exponent': private_exponent,
'prime1': prime1,
'prime2': prime2,
'exponent1': exponent1,
'exponent2': exponent2,
'coefficient': coefficient,
})
private_key_info = PrivateKeyInfo({
'version': 0,
'private_key_algorithm': PrivateKeyAlgorithm({
'algorithm': 'rsa',
}),
'private_key': rsa_private_key,
})
return (public_key_info, private_key_info)
def _advapi32_interpret_dsa_key_blob(bit_size, public_blob, private_blob):
"""
Takes a CryptoAPI DSS private key blob and converts it into the ASN.1
structures for the public and private keys
:param bit_size:
The integer bit size of the key
:param public_blob:
A byte string of the binary data after the public key header
:param private_blob:
A byte string of the binary data after the private key header
:return:
A 2-element tuple of (asn1crypto.keys.PublicKeyInfo,
asn1crypto.keys.PrivateKeyInfo)
"""
len1 = 20
len2 = bit_size // 8
q_offset = len2
g_offset = q_offset + len1
x_offset = g_offset + len2
y_offset = x_offset
p = int_from_bytes(private_blob[0:q_offset][::-1])
q = int_from_bytes(private_blob[q_offset:g_offset][::-1])
g = int_from_bytes(private_blob[g_offset:x_offset][::-1])
x = int_from_bytes(private_blob[x_offset:x_offset + len1][::-1])
y = int_from_bytes(public_blob[y_offset:y_offset + len2][::-1])
public_key_info = PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'dsa',
'parameters': DSAParams({
'p': p,
'q': q,
'g': g,
})
}),
'public_key': Integer(y),
})
private_key_info = PrivateKeyInfo({
'version': 0,
'private_key_algorithm': PrivateKeyAlgorithm({
'algorithm': 'dsa',
'parameters': DSAParams({
'p': p,
'q': q,
'g': g,
})
}),
'private_key': Integer(x),
})
return (public_key_info, private_key_info)
def _bcrypt_interpret_rsa_key_blob(key_type, blob_struct, blob):
"""
Take a CNG BCRYPT_RSAFULLPRIVATE_BLOB and converts it into an ASN.1
structure
:param key_type:
A unicode string of "private" or "public"
:param blob_struct:
An instance of BCRYPT_RSAKEY_BLOB
:param blob:
A byte string of the binary data contained after the struct
:return:
An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo
object, based on the key_type param
"""
public_exponent_byte_length = native(int, blob_struct.cbPublicExp)
modulus_byte_length = native(int, blob_struct.cbModulus)
modulus_offset = public_exponent_byte_length
public_exponent = int_from_bytes(blob[0:modulus_offset])
modulus = int_from_bytes(blob[modulus_offset:modulus_offset + modulus_byte_length])
if key_type == 'public':
return PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'rsa',
}),
'public_key': RSAPublicKey({
'modulus': modulus,
'public_exponent': public_exponent,
}),
})
elif key_type == 'private':
prime1_byte_length = native(int, blob_struct.cbPrime1)
prime2_byte_length = native(int, blob_struct.cbPrime2)
prime1_offset = modulus_offset + modulus_byte_length
prime2_offset = prime1_offset + prime1_byte_length
exponent1_offset = prime2_offset + prime2_byte_length
exponent2_offset = exponent1_offset + prime2_byte_length
coefficient_offset = exponent2_offset + prime2_byte_length
private_exponent_offset = coefficient_offset + prime1_byte_length
prime1 = int_from_bytes(blob[prime1_offset:prime2_offset])
prime2 = int_from_bytes(blob[prime2_offset:exponent1_offset])
exponent1 = int_from_bytes(blob[exponent1_offset:exponent2_offset])
exponent2 = int_from_bytes(blob[exponent2_offset:coefficient_offset])
coefficient = int_from_bytes(blob[coefficient_offset:private_exponent_offset])
private_exponent = int_from_bytes(blob[private_exponent_offset:private_exponent_offset + modulus_byte_length])
rsa_private_key = RSAPrivateKey({
'version': 'two-prime',
'modulus': modulus,
'public_exponent': public_exponent,
'private_exponent': private_exponent,
'prime1': prime1,
'prime2': prime2,
'exponent1': exponent1,
'exponent2': exponent2,
'coefficient': coefficient,
})
return PrivateKeyInfo({
'version': 0,
'private_key_algorithm': PrivateKeyAlgorithm({
'algorithm': 'rsa',
}),
'private_key': rsa_private_key,
})
else:
raise ValueError(pretty_message(
'''
key_type must be one of "public", "private", not %s
''',
repr(key_type)
))
def _bcrypt_interpret_dsa_key_blob(key_type, version, blob_struct, blob):
"""
Take a CNG BCRYPT_DSA_KEY_BLOB or BCRYPT_DSA_KEY_BLOB_V2 and converts it
into an ASN.1 structure
:param key_type:
A unicode string of "private" or "public"
:param version:
An integer - 1 or 2, indicating the blob is BCRYPT_DSA_KEY_BLOB or
BCRYPT_DSA_KEY_BLOB_V2
:param blob_struct:
An instance of BCRYPT_DSA_KEY_BLOB or BCRYPT_DSA_KEY_BLOB_V2
:param blob:
A byte string of the binary data contained after the struct
:return:
An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo
object, based on the key_type param
"""
key_byte_length = native(int, blob_struct.cbKey)
if version == 1:
q = int_from_bytes(native(byte_cls, blob_struct.q))
g_offset = key_byte_length
public_offset = g_offset + key_byte_length
private_offset = public_offset + key_byte_length
p = int_from_bytes(blob[0:g_offset])
g = int_from_bytes(blob[g_offset:public_offset])
elif version == 2:
seed_byte_length = native(int, blob_struct.cbSeedLength)
group_byte_length = native(int, blob_struct.cbGroupSize)
q_offset = seed_byte_length
p_offset = q_offset + group_byte_length
g_offset = p_offset + key_byte_length
public_offset = g_offset + key_byte_length
private_offset = public_offset + key_byte_length
# The seed is skipped since it is not part of the ASN.1 structure
q = int_from_bytes(blob[q_offset:p_offset])
p = int_from_bytes(blob[p_offset:g_offset])
g = int_from_bytes(blob[g_offset:public_offset])
else:
raise ValueError('version must be 1 or 2, not %s' % repr(version))
if key_type == 'public':
public = int_from_bytes(blob[public_offset:private_offset])
return PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'dsa',
'parameters': DSAParams({
'p': p,
'q': q,
'g': g,
})
}),
'public_key': Integer(public),
})
elif key_type == 'private':
private = int_from_bytes(blob[private_offset:private_offset + key_byte_length])
return PrivateKeyInfo({
'version': 0,
'private_key_algorithm': PrivateKeyAlgorithm({
'algorithm': 'dsa',
'parameters': DSAParams({
'p': p,
'q': q,
'g': g,
})
}),
'private_key': Integer(private),
})
else:
raise ValueError(pretty_message(
'''
key_type must be one of "public", "private", not %s
''',
repr(key_type)
))
def _bcrypt_interpret_ec_key_blob(key_type, blob_struct, blob):
"""
Take a CNG BCRYPT_ECCKEY_BLOB and converts it into an ASN.1 structure
:param key_type:
A unicode string of "private" or "public"
:param blob_struct:
An instance of BCRYPT_ECCKEY_BLOB
:param blob:
A byte string of the binary data contained after the struct
:return:
An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo
object, based on the key_type param
"""
magic = native(int, blob_struct.dwMagic)
key_byte_length = native(int, blob_struct.cbKey)
curve = {
BcryptConst.BCRYPT_ECDSA_PRIVATE_P256_MAGIC: 'secp256r1',
BcryptConst.BCRYPT_ECDSA_PRIVATE_P384_MAGIC: 'secp384r1',
BcryptConst.BCRYPT_ECDSA_PRIVATE_P521_MAGIC: 'secp521r1',
BcryptConst.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: 'secp256r1',
BcryptConst.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: 'secp384r1',
BcryptConst.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: 'secp521r1',
}[magic]
public = b'\x04' + blob[0:key_byte_length * 2]
if key_type == 'public':
return PublicKeyInfo({
'algorithm': PublicKeyAlgorithm({
'algorithm': 'ec',
'parameters': ECDomainParameters(
name='named',
value=curve
)
}),
'public_key': public,
})
elif key_type == 'private':
private = int_from_bytes(blob[key_byte_length * 2:key_byte_length * 3])
return PrivateKeyInfo({
'version': 0,
'private_key_algorithm': PrivateKeyAlgorithm({
'algorithm': 'ec',
'parameters': ECDomainParameters(
name='named',
value=curve
)
}),
'private_key': ECPrivateKey({
'version': 'ecPrivkeyVer1',
'private_key': private,
'public_key': public,
}),
})
else:
raise ValueError(pretty_message(
'''
key_type must be one of "public", "private", not %s
''',
repr(key_type)
))
def load_certificate(source):
"""
Loads an x509 certificate into a Certificate object
:param source:
A byte string of file contents or a unicode string filename
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A Certificate object
"""
if isinstance(source, Asn1Certificate):
certificate = source
elif isinstance(source, byte_cls):
certificate = parse_certificate(source)
elif isinstance(source, str_cls):
with open(source, 'rb') as f:
certificate = parse_certificate(f.read())
else:
raise TypeError(pretty_message(
'''
source must be a byte string, unicode string or
asn1crypto.x509.Certificate object, not %s
''',
type_name(source)
))
return _load_key(certificate, Certificate)
def _load_key(key_object, container):
"""
Loads a certificate, public key or private key into a Certificate,
PublicKey or PrivateKey object
:param key_object:
An asn1crypto.x509.Certificate, asn1crypto.keys.PublicKeyInfo or
asn1crypto.keys.PrivateKeyInfo object
:param container:
The class of the object to hold the key_handle
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library
OSError - when an error is returned by the OS crypto library
:return:
A PrivateKey, PublicKey or Certificate object, based on container
"""
key_info = key_object
if isinstance(key_object, Asn1Certificate):
key_info = key_object['tbs_certificate']['subject_public_key_info']
algo = key_info.algorithm
curve_name = None
if algo == 'ec':
curve_type, curve_name = key_info.curve
if curve_type != 'named':
raise AsymmetricKeyError(pretty_message(
'''
Windows only supports EC keys using named curves
'''
))
if curve_name not in set(['secp256r1', 'secp384r1', 'secp521r1']):
raise AsymmetricKeyError(pretty_message(
'''
Windows only supports EC keys using the named curves
secp256r1, secp384r1 and secp521r1
'''
))
elif algo == 'dsa':
if key_info.hash_algo is None:
raise IncompleteAsymmetricKeyError(pretty_message(
'''
The DSA key does not contain the necessary p, q and g
parameters and can not be used
'''
))
elif key_info.bit_size > 1024 and (_win_version_info < (6, 2) or _backend == 'winlegacy'):
raise AsymmetricKeyError(pretty_message(
'''
Windows XP, 2003, Vista, 7 and Server 2008 only support DSA
keys based on SHA1 (1024 bits or less) - this key is based
on %s and is %s bits
''',
key_info.hash_algo.upper(),
key_info.bit_size
))
elif key_info.bit_size == 2048 and key_info.hash_algo == 'sha1':
raise AsymmetricKeyError(pretty_message(
'''
Windows only supports 2048 bit DSA keys based on SHA2 - this
key is 2048 bits and based on SHA1, a non-standard
combination that is usually generated by old versions
of OpenSSL
'''
))
if _backend == 'winlegacy':
if algo == 'ec':
return container(None, key_object)
return _advapi32_load_key(key_object, key_info, container)
return _bcrypt_load_key(key_object, key_info, container, curve_name)
def _advapi32_load_key(key_object, key_info, container):
"""
Loads a certificate, public key or private key into a Certificate,
PublicKey or PrivateKey object via CryptoAPI
:param key_object:
An asn1crypto.x509.Certificate, asn1crypto.keys.PublicKeyInfo or
asn1crypto.keys.PrivateKeyInfo object
:param key_info:
An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo
object
:param container:
The class of the object to hold the key_handle
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library
OSError - when an error is returned by the OS crypto library
:return:
A PrivateKey, PublicKey or Certificate object, based on container
"""
key_type = 'public' if isinstance(key_info, PublicKeyInfo) else 'private'
algo = key_info.algorithm
if algo == 'rsassa_pss':
algo = 'rsa'
if algo == 'rsa' or algo == 'rsassa_pss':
provider = Advapi32Const.MS_ENH_RSA_AES_PROV
else:
provider = Advapi32Const.MS_ENH_DSS_DH_PROV
context_handle = None
key_handle = None
try:
context_handle = open_context_handle(provider, verify_only=key_type == 'public')
blob = _advapi32_create_blob(key_info, key_type, algo)
buffer_ = buffer_from_bytes(blob)
key_handle_pointer = new(advapi32, 'HCRYPTKEY *')
res = advapi32.CryptImportKey(
context_handle,
buffer_,
len(blob),
null(),
0,
key_handle_pointer
)
handle_error(res)
key_handle = unwrap(key_handle_pointer)
output = container(key_handle, key_object)
output.context_handle = context_handle
if algo == 'rsa':
ex_blob = _advapi32_create_blob(key_info, key_type, algo, signing=False)
ex_buffer = buffer_from_bytes(ex_blob)
ex_key_handle_pointer = new(advapi32, 'HCRYPTKEY *')
res = advapi32.CryptImportKey(
context_handle,
ex_buffer,
len(ex_blob),
null(),
0,
ex_key_handle_pointer
)
handle_error(res)
output.ex_key_handle = unwrap(ex_key_handle_pointer)
return output
except (Exception):
if key_handle:
advapi32.CryptDestroyKey(key_handle)
if context_handle:
close_context_handle(context_handle)
raise
def _advapi32_create_blob(key_info, key_type, algo, signing=True):
"""
Generates a blob for importing a key to CryptoAPI
:param key_info:
An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo
object
:param key_type:
A unicode string of "public" or "private"
:param algo:
A unicode string of "rsa" or "dsa"
:param signing:
If the key handle is for signing - may only be False for rsa keys
:return:
A byte string of a blob to pass to advapi32.CryptImportKey()
"""
if key_type == 'public':
blob_type = Advapi32Const.PUBLICKEYBLOB
else:
blob_type = Advapi32Const.PRIVATEKEYBLOB
if algo == 'rsa':
struct_type = 'RSABLOBHEADER'
if signing:
algorithm_id = Advapi32Const.CALG_RSA_SIGN
else:
algorithm_id = Advapi32Const.CALG_RSA_KEYX
else:
struct_type = 'DSSBLOBHEADER'
algorithm_id = Advapi32Const.CALG_DSS_SIGN
blob_header_pointer = struct(advapi32, 'BLOBHEADER')
blob_header = unwrap(blob_header_pointer)
blob_header.bType = blob_type
blob_header.bVersion = Advapi32Const.CUR_BLOB_VERSION
blob_header.reserved = 0
blob_header.aiKeyAlg = algorithm_id
blob_struct_pointer = struct(advapi32, struct_type)
blob_struct = unwrap(blob_struct_pointer)
blob_struct.publickeystruc = blob_header
bit_size = key_info.bit_size
len1 = bit_size // 8
len2 = bit_size // 16
if algo == 'rsa':
pubkey_pointer = struct(advapi32, 'RSAPUBKEY')
pubkey = unwrap(pubkey_pointer)
pubkey.bitlen = bit_size
if key_type == 'public':
parsed_key_info = key_info['public_key'].parsed
pubkey.magic = Advapi32Const.RSA1
pubkey.pubexp = parsed_key_info['public_exponent'].native
blob_data = int_to_bytes(parsed_key_info['modulus'].native, signed=False, width=len1)[::-1]
else:
parsed_key_info = key_info['private_key'].parsed
pubkey.magic = Advapi32Const.RSA2
pubkey.pubexp = parsed_key_info['public_exponent'].native
blob_data = int_to_bytes(parsed_key_info['modulus'].native, signed=False, width=len1)[::-1]
blob_data += int_to_bytes(parsed_key_info['prime1'].native, signed=False, width=len2)[::-1]
blob_data += int_to_bytes(parsed_key_info['prime2'].native, signed=False, width=len2)[::-1]
blob_data += int_to_bytes(parsed_key_info['exponent1'].native, signed=False, width=len2)[::-1]
blob_data += int_to_bytes(parsed_key_info['exponent2'].native, signed=False, width=len2)[::-1]
blob_data += int_to_bytes(parsed_key_info['coefficient'].native, signed=False, width=len2)[::-1]
blob_data += int_to_bytes(parsed_key_info['private_exponent'].native, signed=False, width=len1)[::-1]
blob_struct.rsapubkey = pubkey
else:
pubkey_pointer = struct(advapi32, 'DSSPUBKEY')
pubkey = unwrap(pubkey_pointer)
pubkey.bitlen = bit_size
if key_type == 'public':
pubkey.magic = Advapi32Const.DSS1
params = key_info['algorithm']['parameters'].native
key_data = int_to_bytes(key_info['public_key'].parsed.native, signed=False, width=len1)[::-1]
else:
pubkey.magic = Advapi32Const.DSS2
params = key_info['private_key_algorithm']['parameters'].native
key_data = int_to_bytes(key_info['private_key'].parsed.native, signed=False, width=20)[::-1]
blob_struct.dsspubkey = pubkey
blob_data = int_to_bytes(params['p'], signed=False, width=len1)[::-1]
blob_data += int_to_bytes(params['q'], signed=False, width=20)[::-1]
blob_data += int_to_bytes(params['g'], signed=False, width=len1)[::-1]
blob_data += key_data
dssseed_pointer = struct(advapi32, 'DSSSEED')
dssseed = unwrap(dssseed_pointer)
# This indicates no counter or seed info is available
dssseed.counter = 0xffffffff
blob_data += struct_bytes(dssseed_pointer)
return struct_bytes(blob_struct_pointer) + blob_data
def _bcrypt_load_key(key_object, key_info, container, curve_name):
"""
Loads a certificate, public key or private key into a Certificate,
PublicKey or PrivateKey object via CNG
:param key_object:
An asn1crypto.x509.Certificate, asn1crypto.keys.PublicKeyInfo or
asn1crypto.keys.PrivateKeyInfo object
:param key_info:
An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo
object
:param container:
The class of the object to hold the key_handle
:param curve_name:
None or a unicode string of the curve name for an EC key
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library
OSError - when an error is returned by the OS crypto library
:return:
A PrivateKey, PublicKey or Certificate object, based on container
"""
alg_handle = None
key_handle = None
key_type = 'public' if isinstance(key_info, PublicKeyInfo) else 'private'
algo = key_info.algorithm
if algo == 'rsassa_pss':
algo = 'rsa'
try:
alg_selector = key_info.curve[1] if algo == 'ec' else algo
alg_constant = {
'rsa': BcryptConst.BCRYPT_RSA_ALGORITHM,
'dsa': BcryptConst.BCRYPT_DSA_ALGORITHM,
'secp256r1': BcryptConst.BCRYPT_ECDSA_P256_ALGORITHM,
'secp384r1': BcryptConst.BCRYPT_ECDSA_P384_ALGORITHM,
'secp521r1': BcryptConst.BCRYPT_ECDSA_P521_ALGORITHM,
}[alg_selector]
alg_handle = open_alg_handle(alg_constant)
if algo == 'rsa':
if key_type == 'public':
blob_type = BcryptConst.BCRYPT_RSAPUBLIC_BLOB
magic = BcryptConst.BCRYPT_RSAPUBLIC_MAGIC
parsed_key = key_info['public_key'].parsed
prime1_size = 0
prime2_size = 0
else:
blob_type = BcryptConst.BCRYPT_RSAFULLPRIVATE_BLOB
magic = BcryptConst.BCRYPT_RSAFULLPRIVATE_MAGIC
parsed_key = key_info['private_key'].parsed
prime1 = int_to_bytes(parsed_key['prime1'].native)
prime2 = int_to_bytes(parsed_key['prime2'].native)
exponent1 = int_to_bytes(parsed_key['exponent1'].native)
exponent2 = int_to_bytes(parsed_key['exponent2'].native)
coefficient = int_to_bytes(parsed_key['coefficient'].native)
private_exponent = int_to_bytes(parsed_key['private_exponent'].native)
prime1_size = len(prime1)
prime2_size = len(prime2)
public_exponent = int_to_bytes(parsed_key['public_exponent'].native)
modulus = int_to_bytes(parsed_key['modulus'].native)
blob_struct_pointer = struct(bcrypt, 'BCRYPT_RSAKEY_BLOB')
blob_struct = unwrap(blob_struct_pointer)
blob_struct.Magic = magic
blob_struct.BitLength = key_info.bit_size
blob_struct.cbPublicExp = len(public_exponent)
blob_struct.cbModulus = len(modulus)
blob_struct.cbPrime1 = prime1_size
blob_struct.cbPrime2 = prime2_size
blob = struct_bytes(blob_struct_pointer) + public_exponent + modulus
if key_type == 'private':
blob += prime1 + prime2
blob += fill_width(exponent1, prime1_size)
blob += fill_width(exponent2, prime2_size)
blob += fill_width(coefficient, prime1_size)
blob += fill_width(private_exponent, len(modulus))
elif algo == 'dsa':
if key_type == 'public':
blob_type = BcryptConst.BCRYPT_DSA_PUBLIC_BLOB
public_key = key_info['public_key'].parsed.native
params = key_info['algorithm']['parameters']
else:
blob_type = BcryptConst.BCRYPT_DSA_PRIVATE_BLOB
public_key = _unwrap_private_key_info(key_info)['public_key'].native
private_bytes = int_to_bytes(key_info['private_key'].parsed.native)
params = key_info['private_key_algorithm']['parameters']
public_bytes = int_to_bytes(public_key)
p = int_to_bytes(params['p'].native)
g = int_to_bytes(params['g'].native)
q = int_to_bytes(params['q'].native)
if key_info.bit_size > 1024:
q_len = len(q)
else:
q_len = 20
key_width = max(len(public_bytes), len(g), len(p))
public_bytes = fill_width(public_bytes, key_width)
p = fill_width(p, key_width)
g = fill_width(g, key_width)
q = fill_width(q, q_len)
# We don't know the count or seed, so we set them to the max value
# since setting them to 0 results in a parameter error
count = b'\xff' * 4
seed = b'\xff' * q_len
if key_info.bit_size > 1024:
if key_type == 'public':
magic = BcryptConst.BCRYPT_DSA_PUBLIC_MAGIC_V2
else:
magic = BcryptConst.BCRYPT_DSA_PRIVATE_MAGIC_V2
blob_struct_pointer = struct(bcrypt, 'BCRYPT_DSA_KEY_BLOB_V2')
blob_struct = unwrap(blob_struct_pointer)
blob_struct.dwMagic = magic
blob_struct.cbKey = key_width
# We don't know if SHA256 was used here, but the output is long
# enough for the generation of q for the supported 2048/224,
# 2048/256 and 3072/256 FIPS approved pairs
blob_struct.hashAlgorithm = BcryptConst.DSA_HASH_ALGORITHM_SHA256
blob_struct.standardVersion = BcryptConst.DSA_FIPS186_3
blob_struct.cbSeedLength = q_len
blob_struct.cbGroupSize = q_len
blob_struct.Count = byte_array(count)
blob = struct_bytes(blob_struct_pointer)
blob += seed + q + p + g + public_bytes
if key_type == 'private':
blob += fill_width(private_bytes, q_len)
else:
if key_type == 'public':
magic = BcryptConst.BCRYPT_DSA_PUBLIC_MAGIC
else:
magic = BcryptConst.BCRYPT_DSA_PRIVATE_MAGIC
blob_struct_pointer = struct(bcrypt, 'BCRYPT_DSA_KEY_BLOB')
blob_struct = unwrap(blob_struct_pointer)
blob_struct.dwMagic = magic
blob_struct.cbKey = key_width
blob_struct.Count = byte_array(count)
blob_struct.Seed = byte_array(seed)
blob_struct.q = byte_array(q)
blob = struct_bytes(blob_struct_pointer) + p + g + public_bytes
if key_type == 'private':
blob += fill_width(private_bytes, q_len)
elif algo == 'ec':
if key_type == 'public':
blob_type = BcryptConst.BCRYPT_ECCPUBLIC_BLOB
x, y = key_info['public_key'].to_coords()
else:
blob_type = BcryptConst.BCRYPT_ECCPRIVATE_BLOB
public_key = key_info['private_key'].parsed['public_key']
# We aren't guaranteed to get the public key coords with the
# key info structure, but BCrypt doesn't seem to have an issue
# importing the private key with 0 values, which can only be
# presumed that it is generating the x and y points from the
# private key value and base point
if public_key:
x, y = public_key.to_coords()
else:
x = 0
y = 0
private_bytes = int_to_bytes(key_info['private_key'].parsed['private_key'].native)
blob_struct_pointer = struct(bcrypt, 'BCRYPT_ECCKEY_BLOB')
blob_struct = unwrap(blob_struct_pointer)
magic = {
('public', 'secp256r1'): BcryptConst.BCRYPT_ECDSA_PUBLIC_P256_MAGIC,
('public', 'secp384r1'): BcryptConst.BCRYPT_ECDSA_PUBLIC_P384_MAGIC,
('public', 'secp521r1'): BcryptConst.BCRYPT_ECDSA_PUBLIC_P521_MAGIC,
('private', 'secp256r1'): BcryptConst.BCRYPT_ECDSA_PRIVATE_P256_MAGIC,
('private', 'secp384r1'): BcryptConst.BCRYPT_ECDSA_PRIVATE_P384_MAGIC,
('private', 'secp521r1'): BcryptConst.BCRYPT_ECDSA_PRIVATE_P521_MAGIC,
}[(key_type, curve_name)]
key_width = {
'secp256r1': 32,
'secp384r1': 48,
'secp521r1': 66
}[curve_name]
x_bytes = int_to_bytes(x)
y_bytes = int_to_bytes(y)
x_bytes = fill_width(x_bytes, key_width)
y_bytes = fill_width(y_bytes, key_width)
blob_struct.dwMagic = magic
blob_struct.cbKey = key_width
blob = struct_bytes(blob_struct_pointer) + x_bytes + y_bytes
if key_type == 'private':
blob += fill_width(private_bytes, key_width)
key_handle_pointer = new(bcrypt, 'BCRYPT_KEY_HANDLE *')
res = bcrypt.BCryptImportKeyPair(
alg_handle,
null(),
blob_type,
key_handle_pointer,
blob,
len(blob),
BcryptConst.BCRYPT_NO_KEY_VALIDATION
)
handle_error(res)
key_handle = unwrap(key_handle_pointer)
return container(key_handle, key_object)
finally:
if alg_handle:
close_alg_handle(alg_handle)
def load_private_key(source, password=None):
"""
Loads a private key into a PrivateKey object
:param source:
A byte string of file contents, a unicode string filename or an
asn1crypto.keys.PrivateKeyInfo object
:param password:
A byte or unicode string to decrypt the private key file. Unicode
strings will be encoded using UTF-8. Not used is the source is a
PrivateKeyInfo object.
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
oscrypto.errors.AsymmetricKeyError - when the private key is incompatible with the OS crypto library
OSError - when an error is returned by the OS crypto library
:return:
A PrivateKey object
"""
if isinstance(source, PrivateKeyInfo):
private_object = source
else:
if password is not None:
if isinstance(password, str_cls):
password = password.encode('utf-8')
if not isinstance(password, byte_cls):
raise TypeError(pretty_message(
'''
password must be a byte string, not %s
''',
type_name(password)
))
if isinstance(source, str_cls):
with open(source, 'rb') as f:
source = f.read()
elif not isinstance(source, byte_cls):
raise TypeError(pretty_message(
'''
source must be a byte string, unicode string or
asn1crypto.keys.PrivateKeyInfo object, not %s
''',
type_name(source)
))
private_object = parse_private(source, password)
return _load_key(private_object, PrivateKey)
def load_public_key(source):
"""
Loads a public key into a PublicKey object
:param source:
A byte string of file contents, a unicode string filename or an
asn1crypto.keys.PublicKeyInfo object
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
oscrypto.errors.AsymmetricKeyError - when the public key is incompatible with the OS crypto library
OSError - when an error is returned by the OS crypto library
:return:
A PublicKey object
"""
if isinstance(source, PublicKeyInfo):
public_key = source
elif isinstance(source, byte_cls):
public_key = parse_public(source)
elif isinstance(source, str_cls):
with open(source, 'rb') as f:
public_key = parse_public(f.read())
else:
raise TypeError(pretty_message(
'''
source must be a byte string, unicode string or
asn1crypto.keys.PublicKeyInfo object, not %s
''',
type_name(public_key)
))
return _load_key(public_key, PublicKey)
def parse_pkcs12(data, password=None):
"""
Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys
:param data:
A byte string of a DER-encoded PKCS#12 file
:param password:
A byte string of the password to any encrypted data
:raises:
ValueError - when any of the parameters are of the wrong type or value
OSError - when an error is returned by one of the OS decryption functions
:return:
A three-element tuple of:
1. An asn1crypto.keys.PrivateKeyInfo object
2. An asn1crypto.x509.Certificate object
3. A list of zero or more asn1crypto.x509.Certificate objects that are
"extra" certificates, possibly intermediates from the cert chain
"""
return _parse_pkcs12(data, password, load_private_key)
def load_pkcs12(source, password=None):
"""
Loads a .p12 or .pfx file into a PrivateKey object and one or more
Certificates objects
:param source:
A byte string of file contents or a unicode string filename
:param password:
A byte or unicode string to decrypt the PKCS12 file. Unicode strings
will be encoded using UTF-8.
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
oscrypto.errors.AsymmetricKeyError - when a contained key is incompatible with the OS crypto library
OSError - when an error is returned by the OS crypto library
:return:
A three-element tuple containing (PrivateKey, Certificate, [Certificate, ...])
"""
if password is not None:
if isinstance(password, str_cls):
password = password.encode('utf-8')
if not isinstance(password, byte_cls):
raise TypeError(pretty_message(
'''
password must be a byte string, not %s
''',
type_name(password)
))
if isinstance(source, str_cls):
with open(source, 'rb') as f:
source = f.read()
elif not isinstance(source, byte_cls):
raise TypeError(pretty_message(
'''
source must be a byte string or a unicode string, not %s
''',
type_name(source)
))
key_info, cert_info, extra_certs_info = parse_pkcs12(source, password)
key = None
cert = None
if key_info:
key = _load_key(key_info, PrivateKey)
if cert_info:
cert = _load_key(cert_info.public_key, Certificate)
extra_certs = [_load_key(info.public_key, Certificate) for info in extra_certs_info]
return (key, cert, extra_certs)
def rsa_pkcs1v15_verify(certificate_or_public_key, signature, data, hash_algorithm):
"""
Verifies an RSASSA-PKCS-v1.5 signature.
When the hash_algorithm is "raw", the operation is identical to RSA
public key decryption. That is: the data is not hashed and no ASN.1
structure with an algorithm identifier of the hash algorithm is placed in
the encrypted byte string.
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
if certificate_or_public_key.algorithm != 'rsa':
raise ValueError('The key specified is not an RSA public key')
return _verify(certificate_or_public_key, signature, data, hash_algorithm)
def rsa_pss_verify(certificate_or_public_key, signature, data, hash_algorithm):
"""
Verifies an RSASSA-PSS signature. For the PSS padding the mask gen algorithm
will be mgf1 using the same hash algorithm as the signature. The salt length
with be the length of the hash algorithm, and the trailer field with be the
standard 0xBC byte.
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512"
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
cp_alg = certificate_or_public_key.algorithm
if cp_alg != 'rsa' and cp_alg != 'rsassa_pss':
raise ValueError('The key specified is not an RSA public key')
return _verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=True)
def dsa_verify(certificate_or_public_key, signature, data, hash_algorithm):
"""
Verifies a DSA signature
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512"
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
if certificate_or_public_key.algorithm != 'dsa':
raise ValueError('The key specified is not a DSA public key')
return _verify(certificate_or_public_key, signature, data, hash_algorithm)
def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm):
"""
Verifies an ECDSA signature
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512"
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
if certificate_or_public_key.algorithm != 'ec':
raise ValueError('The key specified is not an EC public key')
return _verify(certificate_or_public_key, signature, data, hash_algorithm)
def _verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False):
"""
Verifies an RSA, DSA or ECDSA signature
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:param rsa_pss_padding:
If PSS padding should be used for RSA keys
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
if not isinstance(certificate_or_public_key, (Certificate, PublicKey)):
raise TypeError(pretty_message(
'''
certificate_or_public_key must be an instance of the Certificate or
PublicKey class, not %s
''',
type_name(certificate_or_public_key)
))
if not isinstance(signature, byte_cls):
raise TypeError(pretty_message(
'''
signature must be a byte string, not %s
''',
type_name(signature)
))
if not isinstance(data, byte_cls):
raise TypeError(pretty_message(
'''
data must be a byte string, not %s
''',
type_name(data)
))
cp_alg = certificate_or_public_key.algorithm
cp_is_rsa = cp_alg == 'rsa' or cp_alg == 'rsassa_pss'
valid_hash_algorithms = set(['md5', 'sha1', 'sha256', 'sha384', 'sha512'])
if cp_is_rsa and not rsa_pss_padding:
valid_hash_algorithms |= set(['raw'])
if hash_algorithm not in valid_hash_algorithms:
valid_hash_algorithms_error = '"md5", "sha1", "sha256", "sha384", "sha512"'
if cp_is_rsa and not rsa_pss_padding:
valid_hash_algorithms_error += ', "raw"'
raise ValueError(pretty_message(
'''
hash_algorithm must be one of %s, not %s
''',
valid_hash_algorithms_error,
repr(hash_algorithm)
))
if not cp_is_rsa and rsa_pss_padding is not False:
raise ValueError(pretty_message(
'''
PSS padding may only be used with RSA keys - signing via a %s key
was requested
''',
cp_alg.upper()
))
if hash_algorithm == 'raw':
if len(data) > certificate_or_public_key.byte_size - 11:
raise ValueError(pretty_message(
'''
data must be 11 bytes shorter than the key size when
hash_algorithm is "raw" - key size is %s bytes, but
data is %s bytes long
''',
certificate_or_public_key.byte_size,
len(data)
))
if _backend == 'winlegacy':
if certificate_or_public_key.algorithm == 'ec':
return _pure_python_ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm)
return _advapi32_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding)
return _bcrypt_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding)
def _advapi32_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False):
"""
Verifies an RSA, DSA or ECDSA signature via CryptoAPI
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:param rsa_pss_padding:
If PSS padding should be used for RSA keys
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
algo = certificate_or_public_key.algorithm
algo_is_rsa = algo == 'rsa' or algo == 'rsassa_pss'
if algo_is_rsa and rsa_pss_padding:
hash_length = {
'sha1': 20,
'sha224': 28,
'sha256': 32,
'sha384': 48,
'sha512': 64
}.get(hash_algorithm, 0)
decrypted_signature = raw_rsa_public_crypt(certificate_or_public_key, signature)
key_size = certificate_or_public_key.bit_size
if not verify_pss_padding(hash_algorithm, hash_length, key_size, data, decrypted_signature):
raise SignatureError('Signature is invalid')
return
if algo_is_rsa and hash_algorithm == 'raw':
padded_plaintext = raw_rsa_public_crypt(certificate_or_public_key, signature)
try:
plaintext = remove_pkcs1v15_signature_padding(certificate_or_public_key.byte_size, padded_plaintext)
if not constant_compare(plaintext, data):
raise ValueError()
except (ValueError):
raise SignatureError('Signature is invalid')
return
hash_handle = None
try:
alg_id = {
'md5': Advapi32Const.CALG_MD5,
'sha1': Advapi32Const.CALG_SHA1,
'sha256': Advapi32Const.CALG_SHA_256,
'sha384': Advapi32Const.CALG_SHA_384,
'sha512': Advapi32Const.CALG_SHA_512,
}[hash_algorithm]
hash_handle_pointer = new(advapi32, 'HCRYPTHASH *')
res = advapi32.CryptCreateHash(
certificate_or_public_key.context_handle,
alg_id,
null(),
0,
hash_handle_pointer
)
handle_error(res)
hash_handle = unwrap(hash_handle_pointer)
res = advapi32.CryptHashData(hash_handle, data, len(data), 0)
handle_error(res)
if algo == 'dsa':
# Windows doesn't use the ASN.1 Sequence for DSA signatures,
# so we have to convert it here for the verification to work
try:
signature = DSASignature.load(signature).to_p1363()
# Switch the two integers so that the reversal later will
# result in the correct order
half_len = len(signature) // 2
signature = signature[half_len:] + signature[:half_len]
except (ValueError, OverflowError, TypeError):
raise SignatureError('Signature is invalid')
# The CryptoAPI expects signatures to be in little endian byte order,
# which is the opposite of other systems, so we must reverse it
reversed_signature = signature[::-1]
res = advapi32.CryptVerifySignatureW(
hash_handle,
reversed_signature,
len(signature),
certificate_or_public_key.key_handle,
null(),
0
)
handle_error(res)
finally:
if hash_handle:
advapi32.CryptDestroyHash(hash_handle)
def _bcrypt_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False):
"""
Verifies an RSA, DSA or ECDSA signature via CNG
:param certificate_or_public_key:
A Certificate or PublicKey instance to verify the signature with
:param signature:
A byte string of the signature to verify
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:param rsa_pss_padding:
If PSS padding should be used for RSA keys
:raises:
oscrypto.errors.SignatureError - when the signature is determined to be invalid
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
"""
if hash_algorithm == 'raw':
digest = data
else:
hash_constant = {
'md5': BcryptConst.BCRYPT_MD5_ALGORITHM,
'sha1': BcryptConst.BCRYPT_SHA1_ALGORITHM,
'sha256': BcryptConst.BCRYPT_SHA256_ALGORITHM,
'sha384': BcryptConst.BCRYPT_SHA384_ALGORITHM,
'sha512': BcryptConst.BCRYPT_SHA512_ALGORITHM
}[hash_algorithm]
digest = getattr(hashlib, hash_algorithm)(data).digest()
padding_info = null()
flags = 0
cp_alg = certificate_or_public_key.algorithm
cp_is_rsa = cp_alg == 'rsa' or cp_alg == 'rsassa_pss'
if cp_is_rsa:
if rsa_pss_padding:
flags = BcryptConst.BCRYPT_PAD_PSS
padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PSS_PADDING_INFO')
padding_info_struct = unwrap(padding_info_struct_pointer)
# This has to be assigned to a variable to prevent cffi from gc'ing it
hash_buffer = buffer_from_unicode(hash_constant)
padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer)
padding_info_struct.cbSalt = len(digest)
else:
flags = BcryptConst.BCRYPT_PAD_PKCS1
padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PKCS1_PADDING_INFO')
padding_info_struct = unwrap(padding_info_struct_pointer)
# This has to be assigned to a variable to prevent cffi from gc'ing it
if hash_algorithm == 'raw':
padding_info_struct.pszAlgId = null()
else:
hash_buffer = buffer_from_unicode(hash_constant)
padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer)
padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer)
else:
# Windows doesn't use the ASN.1 Sequence for DSA/ECDSA signatures,
# so we have to convert it here for the verification to work
try:
signature = DSASignature.load(signature).to_p1363()
except (ValueError, OverflowError, TypeError):
raise SignatureError('Signature is invalid')
res = bcrypt.BCryptVerifySignature(
certificate_or_public_key.key_handle,
padding_info,
digest,
len(digest),
signature,
len(signature),
flags
)
failure = res == BcryptConst.STATUS_INVALID_SIGNATURE
failure = failure or res == BcryptConst.STATUS_INVALID_PARAMETER
if failure:
raise SignatureError('Signature is invalid')
handle_error(res)
def rsa_pkcs1v15_sign(private_key, data, hash_algorithm):
"""
Generates an RSASSA-PKCS-v1.5 signature.
When the hash_algorithm is "raw", the operation is identical to RSA
private key encryption. That is: the data is not hashed and no ASN.1
structure with an algorithm identifier of the hash algorithm is placed in
the encrypted byte string.
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
if private_key.algorithm != 'rsa':
raise ValueError('The key specified is not an RSA private key')
return _sign(private_key, data, hash_algorithm)
def rsa_pss_sign(private_key, data, hash_algorithm):
"""
Generates an RSASSA-PSS signature. For the PSS padding the mask gen
algorithm will be mgf1 using the same hash algorithm as the signature. The
salt length with be the length of the hash algorithm, and the trailer field
with be the standard 0xBC byte.
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512"
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
pkey_alg = private_key.algorithm
if pkey_alg != 'rsa' and pkey_alg != 'rsassa_pss':
raise ValueError('The key specified is not an RSA private key')
return _sign(private_key, data, hash_algorithm, rsa_pss_padding=True)
def dsa_sign(private_key, data, hash_algorithm):
"""
Generates a DSA signature
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512"
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
if private_key.algorithm != 'dsa':
raise ValueError('The key specified is not a DSA private key')
return _sign(private_key, data, hash_algorithm)
def ecdsa_sign(private_key, data, hash_algorithm):
"""
Generates an ECDSA signature
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512"
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
if private_key.algorithm != 'ec':
raise ValueError('The key specified is not an EC private key')
return _sign(private_key, data, hash_algorithm)
def _sign(private_key, data, hash_algorithm, rsa_pss_padding=False):
"""
Generates an RSA, DSA or ECDSA signature
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:param rsa_pss_padding:
If PSS padding should be used for RSA keys
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
if not isinstance(private_key, PrivateKey):
raise TypeError(pretty_message(
'''
private_key must be an instance of PrivateKey, not %s
''',
type_name(private_key)
))
if not isinstance(data, byte_cls):
raise TypeError(pretty_message(
'''
data must be a byte string, not %s
''',
type_name(data)
))
pkey_alg = private_key.algorithm
pkey_is_rsa = pkey_alg == 'rsa' or pkey_alg == 'rsassa_pss'
valid_hash_algorithms = set(['md5', 'sha1', 'sha256', 'sha384', 'sha512'])
if private_key.algorithm == 'rsa' and not rsa_pss_padding:
valid_hash_algorithms |= set(['raw'])
if hash_algorithm not in valid_hash_algorithms:
valid_hash_algorithms_error = '"md5", "sha1", "sha256", "sha384", "sha512"'
if pkey_is_rsa and not rsa_pss_padding:
valid_hash_algorithms_error += ', "raw"'
raise ValueError(pretty_message(
'''
hash_algorithm must be one of %s, not %s
''',
valid_hash_algorithms_error,
repr(hash_algorithm)
))
if not pkey_is_rsa and rsa_pss_padding is not False:
raise ValueError(pretty_message(
'''
PSS padding may only be used with RSA keys - signing via a %s key
was requested
''',
pkey_alg.upper()
))
if hash_algorithm == 'raw':
if len(data) > private_key.byte_size - 11:
raise ValueError(pretty_message(
'''
data must be 11 bytes shorter than the key size when
hash_algorithm is "raw" - key size is %s bytes, but data
is %s bytes long
''',
private_key.byte_size,
len(data)
))
if _backend == 'winlegacy':
if private_key.algorithm == 'ec':
return _pure_python_ecdsa_sign(private_key, data, hash_algorithm)
return _advapi32_sign(private_key, data, hash_algorithm, rsa_pss_padding)
return _bcrypt_sign(private_key, data, hash_algorithm, rsa_pss_padding)
def _advapi32_sign(private_key, data, hash_algorithm, rsa_pss_padding=False):
"""
Generates an RSA, DSA or ECDSA signature via CryptoAPI
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:param rsa_pss_padding:
If PSS padding should be used for RSA keys
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
algo = private_key.algorithm
algo_is_rsa = algo == 'rsa' or algo == 'rsassa_pss'
if algo_is_rsa and hash_algorithm == 'raw':
padded_data = add_pkcs1v15_signature_padding(private_key.byte_size, data)
return raw_rsa_private_crypt(private_key, padded_data)
if algo_is_rsa and rsa_pss_padding:
hash_length = {
'sha1': 20,
'sha224': 28,
'sha256': 32,
'sha384': 48,
'sha512': 64
}.get(hash_algorithm, 0)
padded_data = add_pss_padding(hash_algorithm, hash_length, private_key.bit_size, data)
return raw_rsa_private_crypt(private_key, padded_data)
if private_key.algorithm == 'dsa' and hash_algorithm == 'md5':
raise ValueError(pretty_message(
'''
Windows does not support md5 signatures with DSA keys
'''
))
hash_handle = None
try:
alg_id = {
'md5': Advapi32Const.CALG_MD5,
'sha1': Advapi32Const.CALG_SHA1,
'sha256': Advapi32Const.CALG_SHA_256,
'sha384': Advapi32Const.CALG_SHA_384,
'sha512': Advapi32Const.CALG_SHA_512,
}[hash_algorithm]
hash_handle_pointer = new(advapi32, 'HCRYPTHASH *')
res = advapi32.CryptCreateHash(
private_key.context_handle,
alg_id,
null(),
0,
hash_handle_pointer
)
handle_error(res)
hash_handle = unwrap(hash_handle_pointer)
res = advapi32.CryptHashData(hash_handle, data, len(data), 0)
handle_error(res)
out_len = new(advapi32, 'DWORD *')
res = advapi32.CryptSignHashW(
hash_handle,
Advapi32Const.AT_SIGNATURE,
null(),
0,
null(),
out_len
)
handle_error(res)
buffer_length = deref(out_len)
buffer_ = buffer_from_bytes(buffer_length)
res = advapi32.CryptSignHashW(
hash_handle,
Advapi32Const.AT_SIGNATURE,
null(),
0,
buffer_,
out_len
)
handle_error(res)
output = bytes_from_buffer(buffer_, deref(out_len))
# CryptoAPI outputs the signature in little endian byte order, so we
# must swap it for compatibility with other systems
output = output[::-1]
if algo == 'dsa':
# Switch the two integers because the reversal just before switched
# then
half_len = len(output) // 2
output = output[half_len:] + output[:half_len]
# Windows doesn't use the ASN.1 Sequence for DSA signatures,
# so we have to convert it here for the verification to work
output = DSASignature.from_p1363(output).dump()
return output
finally:
if hash_handle:
advapi32.CryptDestroyHash(hash_handle)
def _bcrypt_sign(private_key, data, hash_algorithm, rsa_pss_padding=False):
"""
Generates an RSA, DSA or ECDSA signature via CNG
:param private_key:
The PrivateKey to generate the signature with
:param data:
A byte string of the data the signature is for
:param hash_algorithm:
A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw"
:param rsa_pss_padding:
If PSS padding should be used for RSA keys
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the signature
"""
if hash_algorithm == 'raw':
digest = data
else:
hash_constant = {
'md5': BcryptConst.BCRYPT_MD5_ALGORITHM,
'sha1': BcryptConst.BCRYPT_SHA1_ALGORITHM,
'sha256': BcryptConst.BCRYPT_SHA256_ALGORITHM,
'sha384': BcryptConst.BCRYPT_SHA384_ALGORITHM,
'sha512': BcryptConst.BCRYPT_SHA512_ALGORITHM
}[hash_algorithm]
digest = getattr(hashlib, hash_algorithm)(data).digest()
padding_info = null()
flags = 0
pkey_alg = private_key.algorithm
pkey_is_rsa = pkey_alg == 'rsa' or pkey_alg == 'rsassa_pss'
if pkey_is_rsa:
if rsa_pss_padding:
hash_length = {
'md5': 16,
'sha1': 20,
'sha256': 32,
'sha384': 48,
'sha512': 64
}[hash_algorithm]
flags = BcryptConst.BCRYPT_PAD_PSS
padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PSS_PADDING_INFO')
padding_info_struct = unwrap(padding_info_struct_pointer)
# This has to be assigned to a variable to prevent cffi from gc'ing it
hash_buffer = buffer_from_unicode(hash_constant)
padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer)
padding_info_struct.cbSalt = hash_length
else:
flags = BcryptConst.BCRYPT_PAD_PKCS1
padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PKCS1_PADDING_INFO')
padding_info_struct = unwrap(padding_info_struct_pointer)
# This has to be assigned to a variable to prevent cffi from gc'ing it
if hash_algorithm == 'raw':
padding_info_struct.pszAlgId = null()
else:
hash_buffer = buffer_from_unicode(hash_constant)
padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer)
padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer)
if pkey_alg == 'dsa' and private_key.bit_size > 1024 and hash_algorithm in set(['md5', 'sha1']):
raise ValueError(pretty_message(
'''
Windows does not support sha1 signatures with DSA keys based on
sha224, sha256 or sha512
'''
))
out_len = new(bcrypt, 'DWORD *')
res = bcrypt.BCryptSignHash(
private_key.key_handle,
padding_info,
digest,
len(digest),
null(),
0,
out_len,
flags
)
handle_error(res)
buffer_len = deref(out_len)
buffer = buffer_from_bytes(buffer_len)
if pkey_is_rsa:
padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer)
res = bcrypt.BCryptSignHash(
private_key.key_handle,
padding_info,
digest,
len(digest),
buffer,
buffer_len,
out_len,
flags
)
handle_error(res)
signature = bytes_from_buffer(buffer, deref(out_len))
if not pkey_is_rsa:
# Windows doesn't use the ASN.1 Sequence for DSA/ECDSA signatures,
# so we have to convert it here for the verification to work
signature = DSASignature.from_p1363(signature).dump()
return signature
def _encrypt(certificate_or_public_key, data, rsa_oaep_padding=False):
"""
Encrypts a value using an RSA public key
:param certificate_or_public_key:
A Certificate or PublicKey instance to encrypt with
:param data:
A byte string of the data to encrypt
:param rsa_oaep_padding:
If OAEP padding should be used instead of PKCS#1 v1.5
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the ciphertext
"""
if not isinstance(certificate_or_public_key, (Certificate, PublicKey)):
raise TypeError(pretty_message(
'''
certificate_or_public_key must be an instance of the Certificate or
PublicKey class, not %s
''',
type_name(certificate_or_public_key)
))
if not isinstance(data, byte_cls):
raise TypeError(pretty_message(
'''
data must be a byte string, not %s
''',
type_name(data)
))
if not isinstance(rsa_oaep_padding, bool):
raise TypeError(pretty_message(
'''
rsa_oaep_padding must be a bool, not %s
''',
type_name(rsa_oaep_padding)
))
if _backend == 'winlegacy':
return _advapi32_encrypt(certificate_or_public_key, data, rsa_oaep_padding)
return _bcrypt_encrypt(certificate_or_public_key, data, rsa_oaep_padding)
def _advapi32_encrypt(certificate_or_public_key, data, rsa_oaep_padding=False):
"""
Encrypts a value using an RSA public key via CryptoAPI
:param certificate_or_public_key:
A Certificate or PublicKey instance to encrypt with
:param data:
A byte string of the data to encrypt
:param rsa_oaep_padding:
If OAEP padding should be used instead of PKCS#1 v1.5
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the ciphertext
"""
flags = 0
if rsa_oaep_padding:
flags = Advapi32Const.CRYPT_OAEP
out_len = new(advapi32, 'DWORD *', len(data))
res = advapi32.CryptEncrypt(
certificate_or_public_key.ex_key_handle,
null(),
True,
flags,
null(),
out_len,
0
)
handle_error(res)
buffer_len = deref(out_len)
buffer = buffer_from_bytes(buffer_len)
write_to_buffer(buffer, data)
pointer_set(out_len, len(data))
res = advapi32.CryptEncrypt(
certificate_or_public_key.ex_key_handle,
null(),
True,
flags,
buffer,
out_len,
buffer_len
)
handle_error(res)
return bytes_from_buffer(buffer, deref(out_len))[::-1]
def _bcrypt_encrypt(certificate_or_public_key, data, rsa_oaep_padding=False):
"""
Encrypts a value using an RSA public key via CNG
:param certificate_or_public_key:
A Certificate or PublicKey instance to encrypt with
:param data:
A byte string of the data to encrypt
:param rsa_oaep_padding:
If OAEP padding should be used instead of PKCS#1 v1.5
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the ciphertext
"""
flags = BcryptConst.BCRYPT_PAD_PKCS1
if rsa_oaep_padding is True:
flags = BcryptConst.BCRYPT_PAD_OAEP
padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_OAEP_PADDING_INFO')
padding_info_struct = unwrap(padding_info_struct_pointer)
# This has to be assigned to a variable to prevent cffi from gc'ing it
hash_buffer = buffer_from_unicode(BcryptConst.BCRYPT_SHA1_ALGORITHM)
padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer)
padding_info_struct.pbLabel = null()
padding_info_struct.cbLabel = 0
padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer)
else:
padding_info = null()
out_len = new(bcrypt, 'ULONG *')
res = bcrypt.BCryptEncrypt(
certificate_or_public_key.key_handle,
data,
len(data),
padding_info,
null(),
0,
null(),
0,
out_len,
flags
)
handle_error(res)
buffer_len = deref(out_len)
buffer = buffer_from_bytes(buffer_len)
res = bcrypt.BCryptEncrypt(
certificate_or_public_key.key_handle,
data,
len(data),
padding_info,
null(),
0,
buffer,
buffer_len,
out_len,
flags
)
handle_error(res)
return bytes_from_buffer(buffer, deref(out_len))
def _decrypt(private_key, ciphertext, rsa_oaep_padding=False):
"""
Encrypts a value using an RSA private key
:param private_key:
A PrivateKey instance to decrypt with
:param ciphertext:
A byte string of the data to decrypt
:param rsa_oaep_padding:
If OAEP padding should be used instead of PKCS#1 v1.5
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the plaintext
"""
if not isinstance(private_key, PrivateKey):
raise TypeError(pretty_message(
'''
private_key must be an instance of the PrivateKey class, not %s
''',
type_name(private_key)
))
if not isinstance(ciphertext, byte_cls):
raise TypeError(pretty_message(
'''
ciphertext must be a byte string, not %s
''',
type_name(ciphertext)
))
if not isinstance(rsa_oaep_padding, bool):
raise TypeError(pretty_message(
'''
rsa_oaep_padding must be a bool, not %s
''',
type_name(rsa_oaep_padding)
))
if _backend == 'winlegacy':
return _advapi32_decrypt(private_key, ciphertext, rsa_oaep_padding)
return _bcrypt_decrypt(private_key, ciphertext, rsa_oaep_padding)
def _advapi32_decrypt(private_key, ciphertext, rsa_oaep_padding=False):
"""
Encrypts a value using an RSA private key via CryptoAPI
:param private_key:
A PrivateKey instance to decrypt with
:param ciphertext:
A byte string of the data to decrypt
:param rsa_oaep_padding:
If OAEP padding should be used instead of PKCS#1 v1.5
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the plaintext
"""
flags = 0
if rsa_oaep_padding:
flags = Advapi32Const.CRYPT_OAEP
ciphertext = ciphertext[::-1]
buffer = buffer_from_bytes(ciphertext)
out_len = new(advapi32, 'DWORD *', len(ciphertext))
res = advapi32.CryptDecrypt(
private_key.ex_key_handle,
null(),
True,
flags,
buffer,
out_len
)
handle_error(res)
return bytes_from_buffer(buffer, deref(out_len))
def _bcrypt_decrypt(private_key, ciphertext, rsa_oaep_padding=False):
"""
Encrypts a value using an RSA private key via CNG
:param private_key:
A PrivateKey instance to decrypt with
:param ciphertext:
A byte string of the data to decrypt
:param rsa_oaep_padding:
If OAEP padding should be used instead of PKCS#1 v1.5
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the plaintext
"""
flags = BcryptConst.BCRYPT_PAD_PKCS1
if rsa_oaep_padding is True:
flags = BcryptConst.BCRYPT_PAD_OAEP
padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_OAEP_PADDING_INFO')
padding_info_struct = unwrap(padding_info_struct_pointer)
# This has to be assigned to a variable to prevent cffi from gc'ing it
hash_buffer = buffer_from_unicode(BcryptConst.BCRYPT_SHA1_ALGORITHM)
padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer)
padding_info_struct.pbLabel = null()
padding_info_struct.cbLabel = 0
padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer)
else:
padding_info = null()
out_len = new(bcrypt, 'ULONG *')
res = bcrypt.BCryptDecrypt(
private_key.key_handle,
ciphertext,
len(ciphertext),
padding_info,
null(),
0,
null(),
0,
out_len,
flags
)
handle_error(res)
buffer_len = deref(out_len)
buffer = buffer_from_bytes(buffer_len)
res = bcrypt.BCryptDecrypt(
private_key.key_handle,
ciphertext,
len(ciphertext),
padding_info,
null(),
0,
buffer,
buffer_len,
out_len,
flags
)
handle_error(res)
return bytes_from_buffer(buffer, deref(out_len))
def rsa_pkcs1v15_encrypt(certificate_or_public_key, data):
"""
Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1
v1.5 padding.
:param certificate_or_public_key:
A PublicKey or Certificate object
:param data:
A byte string, with a maximum length 11 bytes less than the key length
(in bytes)
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the encrypted data
"""
return _encrypt(certificate_or_public_key, data)
def rsa_pkcs1v15_decrypt(private_key, ciphertext):
"""
Decrypts a byte string using an RSA private key. Uses PKCS#1 v1.5 padding.
:param private_key:
A PrivateKey object
:param ciphertext:
A byte string of the encrypted data
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the original plaintext
"""
return _decrypt(private_key, ciphertext)
def rsa_oaep_encrypt(certificate_or_public_key, data):
"""
Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1
OAEP padding with SHA1.
:param certificate_or_public_key:
A PublicKey or Certificate object
:param data:
A byte string, with a maximum length 41 bytes (or more) less than the
key length (in bytes)
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the encrypted data
"""
return _encrypt(certificate_or_public_key, data, rsa_oaep_padding=True)
def rsa_oaep_decrypt(private_key, ciphertext):
"""
Decrypts a byte string using an RSA private key. Uses PKCS#1 OAEP padding
with SHA1.
:param private_key:
A PrivateKey object
:param ciphertext:
A byte string of the encrypted data
:raises:
ValueError - when any of the parameters contain an invalid value
TypeError - when any of the parameters are of the wrong type
OSError - when an error is returned by the OS crypto library
:return:
A byte string of the original plaintext
"""
return _decrypt(private_key, ciphertext, rsa_oaep_padding=True)