os400qc3: support encrypted private keys
PKCS#8 EncryptedPrivateKeyinfo structures are recognized and decoded to get values accepted by the Qc3 crypto library.
This commit is contained in:
parent
b60fb64b17
commit
77d825ac93
157
src/os400qc3.c
157
src/os400qc3.c
@ -1787,19 +1787,144 @@ parse_pbes1(LIBSSH2_SESSION *session, pkcs5params *pkcs5,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pkcs8kek(LIBSSH2_SESSION *session, _libssh2_os400qc3_crypto_ctx **ctx,
|
||||
const unsigned char *data, unsigned int datalen,
|
||||
const unsigned char *passphrase, asn1Element *privkeyinfo)
|
||||
{
|
||||
asn1Element encprivkeyinfo;
|
||||
asn1Element pkcs5alg;
|
||||
pkcs5params pkcs5;
|
||||
size_t pplen;
|
||||
char *cp;
|
||||
unsigned long t;
|
||||
int i;
|
||||
char *dk = NULL;
|
||||
Qc3_Format_ALGD0200_T algd;
|
||||
Qus_EC_t errcode;
|
||||
|
||||
/* Determine if the PKCS#8 data is encrypted and, if so, set-up a
|
||||
key encryption key and algorithm in context.
|
||||
Return 1 if encrypted, 0, if not, -1 if error. */
|
||||
|
||||
*ctx = NULL;
|
||||
privkeyinfo->beg = (char *) data;
|
||||
privkeyinfo->end = privkeyinfo->beg + datalen;
|
||||
|
||||
/* If no passphrase is given, it cannot be an encrypted key. */
|
||||
if (!passphrase || !*passphrase)
|
||||
return 0;
|
||||
|
||||
/* Parse PKCS#8 data, checking if ASN.1 format is PrivateKeyInfo or
|
||||
EncryptedPrivateKeyInfo. */
|
||||
if (getASN1Element(&encprivkeyinfo, privkeyinfo->beg, privkeyinfo->end) !=
|
||||
(char *) data + datalen ||
|
||||
*encprivkeyinfo.header != (ASN1_SEQ | ASN1_CONSTRUCTED))
|
||||
return -1;
|
||||
cp = getASN1Element(&pkcs5alg, encprivkeyinfo.beg, encprivkeyinfo.end);
|
||||
if (!cp)
|
||||
return -1;
|
||||
|
||||
switch (*pkcs5alg.header) {
|
||||
case ASN1_INTEGER: /* Version. */
|
||||
return 0; /* This is a PrivateKeyInfo --> not encrypted. */
|
||||
case ASN1_SEQ | ASN1_CONSTRUCTED: /* AlgorithIdentifier. */
|
||||
break; /* This is an EncryptedPrivateKeyInfo --> encrypted. */
|
||||
default:
|
||||
return -1; /* Unrecognized: error. */
|
||||
}
|
||||
|
||||
/* Get the encrypted key data. */
|
||||
if (getASN1Element(privkeyinfo, cp, encprivkeyinfo.end) !=
|
||||
encprivkeyinfo.end || *privkeyinfo->header != ASN1_OCTET_STRING)
|
||||
return -1;
|
||||
|
||||
/* PKCS#5: parse the PBES AlgorithmIdentifier and recursively get all
|
||||
encryption parameters. */
|
||||
memset((char *) &pkcs5, 0, sizeof pkcs5);
|
||||
if (parse_pkcs5_algorithm(session, &pkcs5, &pkcs5alg, pbestable))
|
||||
return -1;
|
||||
|
||||
/* Compute the derived key. */
|
||||
if ((*pkcs5.kdf)(session, &dk, passphrase, &pkcs5))
|
||||
return -1;
|
||||
|
||||
/* Prepare the algorithm descriptor. */
|
||||
memset((char *) &algd, 0, sizeof algd);
|
||||
algd.Block_Cipher_Alg = pkcs5.cipher;
|
||||
algd.Block_Length = pkcs5.blocksize;
|
||||
algd.Mode = pkcs5.mode;
|
||||
algd.Pad_Option = pkcs5.padopt;
|
||||
algd.Pad_Character = pkcs5.padchar;
|
||||
algd.Effective_Key_Size = pkcs5.effkeysize;
|
||||
memcpy(algd.Init_Vector, pkcs5.iv, pkcs5.ivlen);
|
||||
|
||||
/* Create the key and algorithm context tokens. */
|
||||
*ctx = libssh2_init_crypto_ctx(NULL);
|
||||
if (!*ctx) {
|
||||
LIBSSH2_FREE(session, dk);
|
||||
return -1;
|
||||
}
|
||||
libssh2_init_crypto_ctx(*ctx);
|
||||
set_EC_length(errcode, sizeof errcode);
|
||||
Qc3CreateKeyContext(dk, &pkcs5.dklen, binstring, &algd.Block_Cipher_Alg,
|
||||
qc3clear, NULL, NULL, (*ctx)->key.Key_Context_Token,
|
||||
(char *) &errcode);
|
||||
LIBSSH2_FREE(session, dk);
|
||||
if (errcode.Bytes_Available) {
|
||||
free((char *) *ctx);
|
||||
*ctx = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Qc3CreateAlgorithmContext((char *) &algd, Qc3_Alg_Block_Cipher,
|
||||
(*ctx)->hash.Alg_Context_Token, &errcode);
|
||||
if (errcode.Bytes_Available) {
|
||||
Qc3DestroyKeyContext((*ctx)->key.Key_Context_Token, (char *) &ecnull);
|
||||
free((char *) *ctx);
|
||||
*ctx = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 1; /* Tell it's encrypted. */
|
||||
}
|
||||
|
||||
static int
|
||||
rsapkcs8privkey(LIBSSH2_SESSION *session,
|
||||
const unsigned char *data, unsigned int datalen,
|
||||
const unsigned char *passphrase, void *loadkeydata)
|
||||
{
|
||||
libssh2_rsa_ctx *ctx = (libssh2_rsa_ctx *) loadkeydata;
|
||||
char keyform = Qc3_Clear;
|
||||
char *kek = NULL;
|
||||
char *kea = NULL;
|
||||
_libssh2_os400qc3_crypto_ctx *kekctx;
|
||||
asn1Element pki;
|
||||
int pkilen;
|
||||
Qus_EC_t errcode;
|
||||
|
||||
switch (pkcs8kek(session, &kekctx, data, datalen, passphrase, &pki)) {
|
||||
case 1:
|
||||
keyform = Qc3_Encrypted;
|
||||
kek = kekctx->key.Key_Context_Token;
|
||||
kea = kekctx->hash.Alg_Context_Token;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_EC_length(errcode, sizeof errcode);
|
||||
Qc3CreateKeyContext((unsigned char *) data, (int *) &datalen, berstring,
|
||||
rsaprivate, qc3clear, NULL, NULL,
|
||||
pkilen = pki.end - pki.beg;
|
||||
Qc3CreateKeyContext((unsigned char *) pki.beg, &pkilen, berstring,
|
||||
rsaprivate, &keyform, kek, kea,
|
||||
ctx->key.Key_Context_Token, (char *) &errcode);
|
||||
return errcode.Bytes_Available? -1: 0;
|
||||
if (errcode.Bytes_Available) {
|
||||
if (kekctx)
|
||||
_libssh2_os400qc3_crypto_dtor(kekctx);
|
||||
return -1;
|
||||
}
|
||||
ctx->kek = kekctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
@ -1852,18 +1977,38 @@ rsapkcs8pubkey(LIBSSH2_SESSION *session,
|
||||
int len;
|
||||
char *cp;
|
||||
int i;
|
||||
char keyform = Qc3_Clear;
|
||||
char *kek = NULL;
|
||||
char *kea = NULL;
|
||||
_libssh2_os400qc3_crypto_ctx *kekctx;
|
||||
asn1Element subjpubkeyinfo;
|
||||
asn1Element algorithmid;
|
||||
asn1Element algorithm;
|
||||
asn1Element subjpubkey;
|
||||
asn1Element parameters;
|
||||
asn1Element pki;
|
||||
int pkilen;
|
||||
Qus_EC_t errcode;
|
||||
|
||||
if (!(buf = alloca(datalen)))
|
||||
return -1;
|
||||
|
||||
switch (pkcs8kek(session, &kekctx, data, datalen, passphrase, &pki)) {
|
||||
case 1:
|
||||
keyform = Qc3_Encrypted;
|
||||
kek = kekctx->key.Key_Context_Token;
|
||||
kea = kekctx->hash.Alg_Context_Token;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_EC_length(errcode, sizeof errcode);
|
||||
Qc3ExtractPublicKey((char *) data, (int *) &datalen, berstring, qc3clear,
|
||||
NULL, NULL, buf, (int *) &datalen, &len, &errcode);
|
||||
pkilen = pki.end - pki.beg;
|
||||
Qc3ExtractPublicKey(pki.beg, &pkilen, berstring, &keyform,
|
||||
kek, kea, buf, (int *) &datalen, &len, &errcode);
|
||||
_libssh2_os400qc3_crypto_dtor(kekctx);
|
||||
if (errcode.Bytes_Available)
|
||||
return -1;
|
||||
/* Get the algorithm OID and key data from SubjectPublicKeyInfo. */
|
||||
@ -2108,7 +2253,7 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
|
||||
*method_len = 0;
|
||||
*pubkeydata = NULL;
|
||||
*pubkeydata_len = 0;
|
||||
/* Note: passphrase not supported. */
|
||||
|
||||
ret = load_rsa_private_file(session, privatekey, passphrase,
|
||||
rsapkcs1pubkey, rsapkcs8pubkey, (void *) &p);
|
||||
if (!ret) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user