hunt-jwt/source/hunt/jwt/JwtOpenSSL.d
2020-11-23 18:00:11 +08:00

335 lines
11 KiB
D

module hunt.jwt.JwtOpenSSL;
import deimos.openssl.ssl;
import deimos.openssl.pem;
import deimos.openssl.rsa;
import deimos.openssl.hmac;
import deimos.openssl.err;
import hunt.jwt.Exceptions;
import hunt.jwt.JwtAlgorithm;
EC_KEY* getESKeypair(uint curve_type, string key) {
EC_GROUP* curve;
EVP_PKEY* pktmp;
BIO* bpo;
EC_POINT* pub;
if(null == (curve = EC_GROUP_new_by_curve_name(curve_type)))
throw new Exception("Unsupported curve.");
scope(exit) EC_GROUP_free(curve);
bpo = BIO_new_mem_buf(cast(char*)key.ptr, -1);
if(bpo is null) {
throw new Exception("Can't load the key.");
}
scope(exit) BIO_free(bpo);
pktmp = PEM_read_bio_PrivateKey(bpo, null, null, null);
if(pktmp is null) {
throw new Exception("Can't load the evp_pkey.");
}
scope(exit) EVP_PKEY_free(pktmp);
EC_KEY* eckey;
eckey = EVP_PKEY_get1_EC_KEY(pktmp);
if(eckey is null) {
throw new Exception("Can't convert evp_pkey to EC_KEY.");
}
scope(failure) EC_KEY_free(eckey);
if(1 != EC_KEY_set_group(eckey, curve)) {
throw new Exception("Can't associate group with the key.");
}
const BIGNUM *prv = EC_KEY_get0_private_key(eckey);
if(null == prv) {
throw new Exception("Can't get private key.");
}
pub = EC_POINT_new(curve);
if(null == pub) {
throw new Exception("Can't allocate EC point.");
}
scope(exit) EC_POINT_free(pub);
if (1 != EC_POINT_mul(curve, pub, prv, null, null, null)) {
throw new Exception("Can't calculate public key.");
}
if(1 != EC_KEY_set_public_key(eckey, pub)) {
throw new Exception("Can't set public key.");
}
return eckey;
}
EC_KEY* getESPrivateKey(uint curve_type, string key) {
EC_GROUP* curve;
EVP_PKEY* pktmp;
BIO* bpo;
if(null == (curve = EC_GROUP_new_by_curve_name(curve_type)))
throw new Exception("Unsupported curve.");
scope(exit) EC_GROUP_free(curve);
bpo = BIO_new_mem_buf(cast(char*)key.ptr, -1);
if(bpo is null) {
throw new Exception("Can't load the key.");
}
scope(exit) BIO_free(bpo);
pktmp = PEM_read_bio_PrivateKey(bpo, null, null, null);
if(pktmp is null) {
throw new Exception("Can't load the evp_pkey.");
}
scope(exit) EVP_PKEY_free(pktmp);
EC_KEY * eckey;
eckey = EVP_PKEY_get1_EC_KEY(pktmp);
if(eckey is null) {
throw new Exception("Can't convert evp_pkey to EC_KEY.");
}
scope(failure) EC_KEY_free(eckey);
if(1 != EC_KEY_set_group(eckey, curve)) {
throw new Exception("Can't associate group with the key.");
}
return eckey;
}
EC_KEY* getESPublicKey(uint curve_type, string key) {
EC_GROUP* curve;
if(null == (curve = EC_GROUP_new_by_curve_name(curve_type)))
throw new Exception("Unsupported curve.");
scope(exit) EC_GROUP_free(curve);
EC_KEY* eckey;
BIO* bpo = BIO_new_mem_buf(cast(char*)key.ptr, -1);
if(bpo is null) {
throw new Exception("Can't load the key.");
}
scope(exit) BIO_free(bpo);
eckey = PEM_read_bio_EC_PUBKEY(bpo, null, null, null);
scope(failure) EC_KEY_free(eckey);
if(1 != EC_KEY_set_group(eckey, curve)) {
throw new Exception("Can't associate group with the key.");
}
if(0 == EC_KEY_check_key(eckey))
throw new Exception("Public key is not valid.");
return eckey;
}
string sign(string msg, string key, JwtAlgorithm algo = JwtAlgorithm.HS256) {
ubyte[] sign;
void sign_hs(const(EVP_MD)* evp, uint signLen) {
sign = new ubyte[signLen];
HMAC_CTX ctx;
scope(exit) HMAC_CTX_reset(&ctx);
HMAC_CTX_reset(&ctx);
if(0 == HMAC_Init_ex(&ctx, key.ptr, cast(int)key.length, evp, null)) {
throw new Exception("Can't initialize HMAC context.");
}
if(0 == HMAC_Update(&ctx, cast(const(ubyte)*)msg.ptr, cast(ulong)msg.length)) {
throw new Exception("Can't update HMAC.");
}
if(0 == HMAC_Final(&ctx, cast(ubyte*)sign.ptr, &signLen)) {
throw new Exception("Can't finalize HMAC.");
}
}
void sign_rs(ubyte* hash, int type, uint len, uint signLen) {
sign = new ubyte[len];
RSA* rsa_private = RSA_new();
scope(exit) RSA_free(rsa_private);
BIO* bpo = BIO_new_mem_buf(cast(char*)key.ptr, -1);
if(bpo is null)
throw new Exception("Can't load the key.");
scope(exit) BIO_free(bpo);
RSA* rsa = PEM_read_bio_RSAPrivateKey(bpo, &rsa_private, null, null);
if(rsa is null) {
throw new Exception("Can't create RSA key.");
}
if(0 == RSA_sign(type, hash, signLen, sign.ptr, &signLen, rsa_private)) {
throw new Exception("Can't sign RSA message digest.");
}
}
void sign_es(uint curve_type, ubyte* hash, int hashLen) {
EC_KEY* eckey = getESPrivateKey(curve_type, key);
scope(exit) EC_KEY_free(eckey);
ECDSA_SIG* sig = ECDSA_do_sign(hash, hashLen, eckey);
if(sig is null) {
throw new Exception("Digest sign failed.");
}
scope(exit) ECDSA_SIG_free(sig);
sign = new ubyte[ECDSA_size(eckey)];
ubyte* c = sign.ptr;
if(!i2d_ECDSA_SIG(sig, &c)) {
throw new Exception("Convert sign to DER format failed.");
}
}
switch(algo) {
case JwtAlgorithm.NONE: {
break;
}
case JwtAlgorithm.HS256: {
sign_hs(EVP_sha256(), SHA256_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.HS384: {
sign_hs(EVP_sha384(), SHA384_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.HS512: {
sign_hs(EVP_sha512(), SHA512_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.RS256: {
ubyte[] hash = new ubyte[SHA256_DIGEST_LENGTH];
SHA256(cast(const(ubyte)*)msg.ptr, msg.length, hash.ptr);
sign_rs(hash.ptr, NID_sha256, 256, SHA256_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.RS384: {
ubyte[] hash = new ubyte[SHA384_DIGEST_LENGTH];
SHA384(cast(const(ubyte)*)msg.ptr, msg.length, hash.ptr);
sign_rs(hash.ptr, NID_sha384, 384, SHA384_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.RS512: {
ubyte[] hash = new ubyte[SHA512_DIGEST_LENGTH];
SHA512(cast(const(ubyte)*)msg.ptr, msg.length, hash.ptr);
sign_rs(hash.ptr, NID_sha512, 512, SHA512_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.ES256: {
ubyte[] hash = new ubyte[SHA256_DIGEST_LENGTH];
SHA256(cast(const(ubyte)*)msg.ptr, msg.length, hash.ptr);
sign_es(NID_secp256k1, hash.ptr, SHA256_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.ES384: {
ubyte[] hash = new ubyte[SHA384_DIGEST_LENGTH];
SHA384(cast(const(ubyte)*)msg.ptr, msg.length, hash.ptr);
sign_es(NID_secp384r1, hash.ptr, SHA384_DIGEST_LENGTH);
break;
}
case JwtAlgorithm.ES512: {
ubyte[] hash = new ubyte[SHA512_DIGEST_LENGTH];
SHA512(cast(const(ubyte)*)msg.ptr, msg.length, hash.ptr);
sign_es(NID_secp521r1, hash.ptr, SHA512_DIGEST_LENGTH);
break;
}
default:
throw new SignException("Wrong algorithm.");
}
return cast(string)sign;
}
bool verifySignature(string signature, string signing_input, string key, JwtAlgorithm algo = JwtAlgorithm.HS256) {
bool verify_rs(ubyte* hash, int type, uint len, uint signLen) {
RSA* rsa_public = RSA_new();
scope(exit) RSA_free(rsa_public);
BIO* bpo = BIO_new_mem_buf(cast(char*)key.ptr, -1);
if(bpo is null)
throw new Exception("Can't load key to the BIO.");
scope(exit) BIO_free(bpo);
RSA* rsa = PEM_read_bio_RSA_PUBKEY(bpo, &rsa_public, null, null);
if(rsa is null) {
throw new Exception("Can't create RSA key.");
}
ubyte[] sign = cast(ubyte[])signature;
int ret = RSA_verify(type, hash, signLen, sign.ptr, len, rsa_public);
return ret == 1;
}
bool verify_es(uint curve_type, ubyte* hash, int hashLen ) {
EC_KEY* eckey = getESPublicKey(curve_type, key);
scope(exit) EC_KEY_free(eckey);
ubyte* c = cast(ubyte*)signature.ptr;
ECDSA_SIG* sig = null;
sig = d2i_ECDSA_SIG(&sig, cast(const (ubyte)**)&c, cast(int) key.length);
if (sig is null) {
throw new Exception("Can't decode ECDSA signature.");
}
scope(exit) ECDSA_SIG_free(sig);
int ret = ECDSA_do_verify(hash, hashLen, sig, eckey);
return ret == 1;
}
switch(algo) {
case JwtAlgorithm.NONE: {
return key.length == 0;
}
case JwtAlgorithm.HS256:
case JwtAlgorithm.HS384:
case JwtAlgorithm.HS512: {
return signature == sign(signing_input, key, algo);
}
case JwtAlgorithm.RS256: {
ubyte[] hash = new ubyte[SHA256_DIGEST_LENGTH];
SHA256(cast(const(ubyte)*)signing_input.ptr, signing_input.length, hash.ptr);
return verify_rs(hash.ptr, NID_sha256, 256, SHA256_DIGEST_LENGTH);
}
case JwtAlgorithm.RS384: {
ubyte[] hash = new ubyte[SHA384_DIGEST_LENGTH];
SHA384(cast(const(ubyte)*)signing_input.ptr, signing_input.length, hash.ptr);
return verify_rs(hash.ptr, NID_sha384, 384, SHA384_DIGEST_LENGTH);
}
case JwtAlgorithm.RS512: {
ubyte[] hash = new ubyte[SHA512_DIGEST_LENGTH];
SHA512(cast(const(ubyte)*)signing_input.ptr, signing_input.length, hash.ptr);
return verify_rs(hash.ptr, NID_sha512, 512, SHA512_DIGEST_LENGTH);
}
case JwtAlgorithm.ES256:{
ubyte[] hash = new ubyte[SHA256_DIGEST_LENGTH];
SHA256(cast(const(ubyte)*)signing_input.ptr, signing_input.length, hash.ptr);
return verify_es(NID_secp256k1, hash.ptr, SHA256_DIGEST_LENGTH );
}
case JwtAlgorithm.ES384:{
ubyte[] hash = new ubyte[SHA384_DIGEST_LENGTH];
SHA384(cast(const(ubyte)*)signing_input.ptr, signing_input.length, hash.ptr);
return verify_es(NID_secp384r1, hash.ptr, SHA384_DIGEST_LENGTH );
}
case JwtAlgorithm.ES512: {
ubyte[] hash = new ubyte[SHA512_DIGEST_LENGTH];
SHA512(cast(const(ubyte)*)signing_input.ptr, signing_input.length, hash.ptr);
return verify_es(NID_secp521r1, hash.ptr, SHA512_DIGEST_LENGTH );
}
default:
throw new VerifyException("Wrong algorithm.");
}
}