mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
* protect-tool.c: New options --have-cert and --prompt.
(export_p12_file): Read a certificate from STDIN and pass it to p12_build. Detect a keygrip and construct the filename in that case. Unprotcet a key if needed. Print error messages for key formats we can't handle. (release_passphrase): New. (get_passphrase): New arg PROMPTNO. Return the allocated string. Changed all callers. * minip12.c: Revamped the build part. (p12_build): New args CERT and CERTLEN. * simple-pwquery.c (agent_open): Don't mangle INFOSTR. * export.c (export_p12, popen_protect_tool) (gpgsm_p12_export): New. * gpgsm.c (main): New command --export-secret-key-p12.
This commit is contained in:
parent
50ad027c9a
commit
a1b487a17a
2
NEWS
2
NEWS
@ -7,6 +7,8 @@ Noteworthy changes in version 1.9.5
|
||||
|
||||
* [gpgsm] The --import command is now able to autodetect pkcs#12
|
||||
files and import secret and private keys from this file format.
|
||||
A new command --export-secret-key-p12 is provided to allow
|
||||
exporting of secret keys in PKCS\#12 format.
|
||||
|
||||
* [gpgsm] The pinentry will now present a description of the key for
|
||||
whom the passphrase is requests.
|
||||
|
@ -1,3 +1,17 @@
|
||||
2004-02-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c: New options --have-cert and --prompt.
|
||||
(export_p12_file): Read a certificate from STDIN and pass it to
|
||||
p12_build. Detect a keygrip and construct the filename in that
|
||||
case. Unprotcet a key if needed. Print error messages for key
|
||||
formats we can't handle.
|
||||
(release_passphrase): New.
|
||||
(get_passphrase): New arg PROMPTNO. Return the allocated
|
||||
string. Changed all callers.
|
||||
|
||||
* minip12.c: Revamped the build part.
|
||||
(p12_build): New args CERT and CERTLEN.
|
||||
|
||||
2004-02-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c (main): Setup the used character set.
|
||||
|
581
agent/minip12.c
581
agent/minip12.c
@ -106,12 +106,19 @@ static unsigned char const oid_rsaEncryption[9] = {
|
||||
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
|
||||
|
||||
|
||||
static unsigned char const data_3desiter1024[30] = {
|
||||
static unsigned char const data_3desiter2048[30] = {
|
||||
0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E,
|
||||
0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x02, 0x02, 0x04, 0x00 };
|
||||
#define DATA_3DESITER1024_SALT_OFF 18
|
||||
0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
|
||||
#define DATA_3DESITER2048_SALT_OFF 18
|
||||
|
||||
static unsigned char const data_rc2iter2048[30] = {
|
||||
0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E,
|
||||
0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
|
||||
#define DATA_RC2ITER2048_SALT_OFF 18
|
||||
|
||||
|
||||
struct buffer_s
|
||||
@ -346,17 +353,22 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
|
||||
if (rc)
|
||||
{
|
||||
log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
|
||||
wipememory (buffer, length);
|
||||
return;
|
||||
}
|
||||
if (set_key_iv (chd, salt, iter, pw,
|
||||
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
|
||||
{
|
||||
wipememory (buffer, length);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0)
|
||||
: gcry_cipher_decrypt (chd, buffer, length, NULL, 0);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
wipememory (buffer, length);
|
||||
log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
@ -474,6 +486,13 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
|
||||
startoffset = 0;
|
||||
buffer = p = plain;
|
||||
|
||||
/* { */
|
||||
/* FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */
|
||||
/* if (!fp || fwrite (p, n, 1, fp) != 1) */
|
||||
/* exit (2); */
|
||||
/* fclose (fp); */
|
||||
/* } */
|
||||
|
||||
where = "outer.outer.seq";
|
||||
if (parse_tag (&p, &n, &ti))
|
||||
goto bailout;
|
||||
@ -708,13 +727,6 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
|
||||
startoffset = 0;
|
||||
buffer = p = plain;
|
||||
|
||||
/* { */
|
||||
/* FILE *fp = fopen ("tmp-3des-plain.der", "wb"); */
|
||||
/* if (!fp || fwrite (p, n, 1, fp) != 1) */
|
||||
/* exit (2); */
|
||||
/* fclose (fp); */
|
||||
/* } */
|
||||
|
||||
where = "decrypted-text";
|
||||
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
|
||||
goto bailout;
|
||||
@ -970,37 +982,48 @@ create_final (struct buffer_s *sequences, size_t *r_length)
|
||||
{
|
||||
int i;
|
||||
size_t needed = 0;
|
||||
size_t n, outseqlen, notsooutseqlen, out0taglen, octstrlen, inseqlen;
|
||||
size_t len[8], n;
|
||||
unsigned char *result, *p;
|
||||
size_t resultlen;
|
||||
|
||||
/* 8 steps to create the pkcs#12 Krampf. */
|
||||
|
||||
/* 7. All the buffers. */
|
||||
for (i=0; sequences[i].buffer; i++)
|
||||
needed += sequences[i].length;
|
||||
/* This goes into a sequences. */
|
||||
inseqlen = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
/* And encapsulate all in an octet string. */
|
||||
octstrlen = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
/* And tag it with [0]. */
|
||||
out0taglen = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
/* Prepend an data OID. */
|
||||
needed += 2 + DIM (oid_data);
|
||||
/* This all into a sequences. */
|
||||
notsooutseqlen = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
/* Prepend the version integer 3. */
|
||||
needed += 3;
|
||||
/* And the final sequence. */
|
||||
outseqlen = needed;
|
||||
|
||||
/* 6. This goes into a sequences. */
|
||||
len[6] = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
|
||||
/* 5. Encapsulate all in an octet string. */
|
||||
len[5] = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
|
||||
/* 4. And tag it with [0]. */
|
||||
len[4] = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
|
||||
/* 3. Prepend an data OID. */
|
||||
needed += 2 + DIM (oid_data);
|
||||
|
||||
/* 2. Put all into a sequences. */
|
||||
len[2] = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
|
||||
/* 1. Prepend the version integer 3. */
|
||||
needed += 3;
|
||||
|
||||
/* 0. And the final outer sequence. */
|
||||
len[0] = needed;
|
||||
n = compute_tag_length (needed);
|
||||
needed += n;
|
||||
|
||||
/* Allocate a buffer. */
|
||||
result = gcry_malloc (needed);
|
||||
if (!result)
|
||||
{
|
||||
@ -1009,25 +1032,32 @@ create_final (struct buffer_s *sequences, size_t *r_length)
|
||||
}
|
||||
p = result;
|
||||
|
||||
/* Store the very outer sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
|
||||
/* Store the version integer 3. */
|
||||
/* 0. Store the very outer sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
|
||||
|
||||
/* 1. Store the version integer 3. */
|
||||
*p++ = TAG_INTEGER;
|
||||
*p++ = 1;
|
||||
*p++ = 3;
|
||||
/* Store another sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, notsooutseqlen);
|
||||
/* Store the data OID. */
|
||||
|
||||
/* 2. Store another sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[2]);
|
||||
|
||||
/* 3. Store the data OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
|
||||
memcpy (p, oid_data, DIM (oid_data));
|
||||
p += DIM (oid_data);
|
||||
/* Next comes a context tag. */
|
||||
p = store_tag_length (p, 0xa0, out0taglen);
|
||||
/* And an octet string. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
|
||||
/* And the inner sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
|
||||
/* And append all the buffers. */
|
||||
|
||||
/* 4. Next comes a context tag. */
|
||||
p = store_tag_length (p, 0xa0, len[4]);
|
||||
|
||||
/* 5. And an octet string. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, len[5]);
|
||||
|
||||
/* 6. And the inner sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[6]);
|
||||
|
||||
/* 7. Append all the buffers. */
|
||||
for (i=0; sequences[i].buffer; i++)
|
||||
{
|
||||
memcpy (p, sequences[i].buffer, sequences[i].length);
|
||||
@ -1044,20 +1074,38 @@ create_final (struct buffer_s *sequences, size_t *r_length)
|
||||
}
|
||||
|
||||
|
||||
/* Expect the RSA key parameters in KPARMS and a password in
|
||||
PW. Create a PKCS structure from it and return it as well as the
|
||||
length in R_LENGTH; return NULL in case of an error. */
|
||||
unsigned char *
|
||||
p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
|
||||
/* Build a DER encoded SEQUENCE with the key:
|
||||
|
||||
SEQUENCE {
|
||||
INTEGER 0
|
||||
SEQUENCE {
|
||||
OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
|
||||
NULL
|
||||
}
|
||||
OCTET STRING, encapsulates {
|
||||
SEQUENCE {
|
||||
INTEGER 0
|
||||
INTEGER
|
||||
INTEGER
|
||||
INTEGER
|
||||
INTEGER
|
||||
INTEGER
|
||||
INTEGER
|
||||
INTEGER
|
||||
INTEGER
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static unsigned char *
|
||||
build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
|
||||
{
|
||||
int rc, i;
|
||||
size_t needed, n;
|
||||
unsigned char *plain, *p, *cipher;
|
||||
size_t plainlen, cipherlen;
|
||||
unsigned char *plain, *p;
|
||||
size_t plainlen;
|
||||
size_t outseqlen, oidseqlen, octstrlen, inseqlen;
|
||||
size_t out0taglen, in0taglen, outoctstrlen;
|
||||
size_t aseq1len, aseq2len, aseq3len;
|
||||
char salt[8];
|
||||
|
||||
needed = 3; /* The version(?) integer of value 0. */
|
||||
for (i=0; kparms[i]; i++)
|
||||
@ -1165,105 +1213,380 @@ p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
|
||||
for (;(plainlen % 8); plainlen++)
|
||||
*p++ = n;
|
||||
|
||||
/* { */
|
||||
/* FILE *fp = fopen("inner-out.der", "wb"); */
|
||||
/* fwrite (plain, 1, plainlen, fp); */
|
||||
/* fclose (fp); */
|
||||
/* } */
|
||||
*r_length = plainlen;
|
||||
return plain;
|
||||
}
|
||||
|
||||
|
||||
/* Encrypt it and prepend a lot of stupid things. */
|
||||
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
|
||||
crypt_block (plain, plainlen, salt, 1024, pw, GCRY_CIPHER_3DES, 1);
|
||||
/* the data goes into an octet string. */
|
||||
needed = compute_tag_length (plainlen);
|
||||
needed += plainlen;
|
||||
/* we prepend the the algorithm identifier (we use a pre-encoded one)*/
|
||||
needed += DIM (data_3desiter1024);
|
||||
/* we put a sequence around. */
|
||||
aseq3len = needed;
|
||||
|
||||
static unsigned char *
|
||||
build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
|
||||
size_t *r_length)
|
||||
{
|
||||
size_t len[11], needed;
|
||||
unsigned char *p, *keybag;
|
||||
size_t keybaglen;
|
||||
|
||||
/* Walk 11 steps down to collect the info: */
|
||||
|
||||
/* 10. The data goes into an octet string. */
|
||||
needed = compute_tag_length (buflen);
|
||||
needed += buflen;
|
||||
|
||||
/* 9. Prepend the algorithm identifier. */
|
||||
needed += DIM (data_3desiter2048);
|
||||
|
||||
/* 8. Put a sequence around. */
|
||||
len[8] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
/* Prepend it with a [0] tag. */
|
||||
in0taglen = needed;
|
||||
|
||||
/* 7. Prepend a [0] tag. */
|
||||
len[7] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
/* Prepend that shroudedKeyBag OID. */
|
||||
|
||||
/* 6. Prepend the shroudedKeyBag OID. */
|
||||
needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);
|
||||
/* Put it all into two sequence. */
|
||||
aseq2len = needed;
|
||||
|
||||
/* 5+4. Put all into two sequences. */
|
||||
len[5] = needed;
|
||||
needed += compute_tag_length ( needed);
|
||||
aseq1len = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
/* This all goes into an octet string. */
|
||||
outoctstrlen = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
/* Prepend it with a [0] tag. */
|
||||
out0taglen = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
/* Prepend the data OID. */
|
||||
needed += 2 + DIM (oid_data);
|
||||
/* And a sequence. */
|
||||
outseqlen = needed;
|
||||
len[4] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
cipher = gcry_malloc (needed);
|
||||
if (!cipher)
|
||||
/* 3. This all goes into an octet string. */
|
||||
len[3] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 2. Prepend another [0] tag. */
|
||||
len[2] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 1. Prepend the data OID. */
|
||||
needed += 2 + DIM (oid_data);
|
||||
|
||||
/* 0. Prepend another sequence. */
|
||||
len[0] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* Now that we have all length information, allocate a buffer. */
|
||||
p = keybag = gcry_malloc (needed);
|
||||
if (!keybag)
|
||||
{
|
||||
log_error ("error allocating buffer\n");
|
||||
gcry_free (plain);
|
||||
return NULL;
|
||||
}
|
||||
p = cipher;
|
||||
/* Store the first sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
|
||||
/* Store the data OID. */
|
||||
|
||||
/* Walk 11 steps up to store the data. */
|
||||
|
||||
/* 0. Store the first sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
|
||||
|
||||
/* 1. Store the data OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
|
||||
memcpy (p, oid_data, DIM (oid_data));
|
||||
p += DIM (oid_data);
|
||||
/* Next comes a context tag. */
|
||||
p = store_tag_length (p, 0xa0, out0taglen);
|
||||
/* And an octet string. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, outoctstrlen);
|
||||
/* Two sequences. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, aseq1len);
|
||||
p = store_tag_length (p, TAG_SEQUENCE, aseq2len);
|
||||
/* Store the shroudedKeyBag OID. */
|
||||
|
||||
/* 2. Store a [0] tag. */
|
||||
p = store_tag_length (p, 0xa0, len[2]);
|
||||
|
||||
/* 3. And an octet string. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, len[3]);
|
||||
|
||||
/* 4+5. Two sequences. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[4]);
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[5]);
|
||||
|
||||
/* 6. Store the shroudedKeyBag OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID,
|
||||
DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
|
||||
memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
|
||||
DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
|
||||
p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);
|
||||
/* Next comes a context tag. */
|
||||
p = store_tag_length (p, 0xa0, in0taglen);
|
||||
/* And a sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, aseq3len);
|
||||
/* Now for the pre-encoded algorithm indentifier and the salt. */
|
||||
memcpy (p, data_3desiter1024, DIM (data_3desiter1024));
|
||||
memcpy (p + DATA_3DESITER1024_SALT_OFF, salt, 8);
|
||||
p += DIM (data_3desiter1024);
|
||||
/* And finally the octet string with the encrypted data. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, plainlen);
|
||||
memcpy (p, plain, plainlen);
|
||||
p += plainlen;
|
||||
cipherlen = p - cipher;
|
||||
|
||||
if (needed != cipherlen)
|
||||
log_debug ("length mismatch: %u, %u\n", needed, cipherlen);
|
||||
gcry_free (plain);
|
||||
/* 7. Store a [0] tag. */
|
||||
p = store_tag_length (p, 0xa0, len[7]);
|
||||
|
||||
{
|
||||
struct buffer_s seqlist[2];
|
||||
/* 8. Store a sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[8]);
|
||||
|
||||
seqlist[0].buffer = cipher;
|
||||
seqlist[0].length = cipherlen;
|
||||
seqlist[1].buffer = NULL;
|
||||
seqlist[1].length = 0;
|
||||
/* 9. Now for the pre-encoded algorithm identifier and the salt. */
|
||||
memcpy (p, data_3desiter2048, DIM (data_3desiter2048));
|
||||
memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8);
|
||||
p += DIM (data_3desiter2048);
|
||||
|
||||
cipher = create_final (seqlist, &cipherlen);
|
||||
gcry_free (seqlist[0].buffer);
|
||||
/* 10. And finally the octet string with the encrypted data. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, buflen);
|
||||
memcpy (p, buffer, buflen);
|
||||
p += buflen;
|
||||
keybaglen = p - keybag;
|
||||
|
||||
if (needed != keybaglen)
|
||||
log_debug ("length mismatch: %u, %u\n", needed, keybaglen);
|
||||
|
||||
*r_length = keybaglen;
|
||||
return keybag;
|
||||
}
|
||||
|
||||
*r_length = cipherlen;
|
||||
return cipher;
|
||||
|
||||
static unsigned char *
|
||||
build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
|
||||
size_t *r_length)
|
||||
{
|
||||
size_t len[9], needed;
|
||||
unsigned char *p, *certbag;
|
||||
size_t certbaglen;
|
||||
|
||||
/* Walk 9 steps down to collect the info: */
|
||||
|
||||
/* 8. The data goes into an octet string. */
|
||||
needed = compute_tag_length (buflen);
|
||||
needed += buflen;
|
||||
|
||||
/* 7. The algorithm identifier. */
|
||||
needed += DIM (data_rc2iter2048);
|
||||
|
||||
/* 6. The data OID. */
|
||||
needed += 2 + DIM (oid_data);
|
||||
|
||||
/* 5. A sequence. */
|
||||
len[5] = needed;
|
||||
needed += compute_tag_length ( needed);
|
||||
|
||||
/* 4. An integer. */
|
||||
needed += 3;
|
||||
|
||||
/* 3. A sequence. */
|
||||
len[3] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 2. A [0] tag. */
|
||||
len[2] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 1. The encryptedData OID. */
|
||||
needed += 2 + DIM (oid_encryptedData);
|
||||
|
||||
/* 0. The first sequence. */
|
||||
len[0] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* Now that we have all length information, allocate a buffer. */
|
||||
p = certbag = gcry_malloc (needed);
|
||||
if (!certbag)
|
||||
{
|
||||
log_error ("error allocating buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Walk 9 steps up to store the data. */
|
||||
|
||||
/* 0. Store the first sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
|
||||
|
||||
/* 1. Store the encryptedData OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData));
|
||||
memcpy (p, oid_encryptedData, DIM (oid_encryptedData));
|
||||
p += DIM (oid_encryptedData);
|
||||
|
||||
/* 2. Store a [0] tag. */
|
||||
p = store_tag_length (p, 0xa0, len[2]);
|
||||
|
||||
/* 3. Store a sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[3]);
|
||||
|
||||
/* 4. Store the integer 0. */
|
||||
*p++ = TAG_INTEGER;
|
||||
*p++ = 1;
|
||||
*p++ = 0;
|
||||
|
||||
/* 5. Store a sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[5]);
|
||||
|
||||
/* 6. Store the data OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
|
||||
memcpy (p, oid_data, DIM (oid_data));
|
||||
p += DIM (oid_data);
|
||||
|
||||
/* 7. Now for the pre-encoded algorithm identifier and the salt. */
|
||||
memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048));
|
||||
memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8);
|
||||
p += DIM (data_rc2iter2048);
|
||||
|
||||
/* 8. And finally the [0] tag with the encrypted data. */
|
||||
p = store_tag_length (p, 0xa0, buflen);
|
||||
memcpy (p, buffer, buflen);
|
||||
p += buflen;
|
||||
certbaglen = p - certbag;
|
||||
|
||||
if (needed != certbaglen)
|
||||
log_debug ("length mismatch: %u, %u\n", needed, certbaglen);
|
||||
|
||||
*r_length = certbaglen;
|
||||
return certbag;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char *
|
||||
build_cert_sequence (unsigned char *buffer, size_t buflen, size_t *r_length)
|
||||
{
|
||||
size_t len[8], needed, n;
|
||||
unsigned char *p, *certseq;
|
||||
size_t certseqlen;
|
||||
|
||||
/* Walk 8 steps down to collect the info: */
|
||||
|
||||
/* 7. The data goes into an octet string. */
|
||||
needed = compute_tag_length (buflen);
|
||||
needed += buflen;
|
||||
|
||||
/* 6. A [0] tag. */
|
||||
len[6] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 5. An OID. */
|
||||
needed += 2 + DIM (oid_x509Certificate_for_pkcs_12);
|
||||
|
||||
/* 4. A sequence. */
|
||||
len[4] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 3. A [0] tag. */
|
||||
len[3] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 2. An OID. */
|
||||
needed += 2 + DIM (oid_pkcs_12_CertBag);
|
||||
|
||||
/* 1. A sequence. */
|
||||
len[1] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* 0. The first sequence. */
|
||||
len[0] = needed;
|
||||
needed += compute_tag_length (needed);
|
||||
|
||||
/* Now that we have all length information, allocate a buffer. */
|
||||
p = certseq = gcry_malloc (needed + 8 /*(for padding)*/);
|
||||
if (!certseq)
|
||||
{
|
||||
log_error ("error allocating buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Walk 8 steps up to store the data. */
|
||||
|
||||
/* 0. Store the first sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
|
||||
|
||||
/* 1. Store the second sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[1]);
|
||||
|
||||
/* 2. Store the pkcs12-cert-bag OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag));
|
||||
memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag));
|
||||
p += DIM (oid_pkcs_12_CertBag);
|
||||
|
||||
/* 3. Store a [0] tag. */
|
||||
p = store_tag_length (p, 0xa0, len[3]);
|
||||
|
||||
/* 4. Store a sequence. */
|
||||
p = store_tag_length (p, TAG_SEQUENCE, len[4]);
|
||||
|
||||
/* 5. Store the x509Certificate OID. */
|
||||
p = store_tag_length (p, TAG_OBJECT_ID,
|
||||
DIM (oid_x509Certificate_for_pkcs_12));
|
||||
memcpy (p, oid_x509Certificate_for_pkcs_12,
|
||||
DIM (oid_x509Certificate_for_pkcs_12));
|
||||
p += DIM (oid_x509Certificate_for_pkcs_12);
|
||||
|
||||
/* 6. Store a [0] tag. */
|
||||
p = store_tag_length (p, 0xa0, len[6]);
|
||||
|
||||
/* 7. And finally the octet string with the actual certificate. */
|
||||
p = store_tag_length (p, TAG_OCTET_STRING, buflen);
|
||||
memcpy (p, buffer, buflen);
|
||||
p += buflen;
|
||||
certseqlen = p - certseq;
|
||||
|
||||
if (needed != certseqlen)
|
||||
log_debug ("length mismatch: %u, %u\n", needed, certseqlen);
|
||||
|
||||
/* Append some pad characters; we already allocated extra space. */
|
||||
n = 8 - certseqlen % 8;
|
||||
for (;(certseqlen % 8); certseqlen++)
|
||||
*p++ = n;
|
||||
|
||||
*r_length = certseqlen;
|
||||
return certseq;
|
||||
}
|
||||
|
||||
|
||||
/* Expect the RSA key parameters in KPARMS and a password in
|
||||
PW. Create a PKCS structure from it and return it as well as the
|
||||
length in R_LENGTH; return NULL in case of an error. */
|
||||
unsigned char *
|
||||
p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
|
||||
const char *pw, size_t *r_length)
|
||||
{
|
||||
unsigned char *buffer;
|
||||
size_t n, buflen;
|
||||
char salt[8];
|
||||
struct buffer_s seqlist[2];
|
||||
int seqlistidx = 0;
|
||||
|
||||
if (cert && certlen)
|
||||
{
|
||||
/* Encode the certificate. */
|
||||
buffer = build_cert_sequence (cert, certlen, &buflen);
|
||||
if (!buffer)
|
||||
goto failure;
|
||||
|
||||
/* Encrypt it. */
|
||||
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
|
||||
crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_RFC2268_40, 1);
|
||||
|
||||
/* Encode the encrypted stuff into a bag. */
|
||||
seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
|
||||
seqlist[seqlistidx].length = n;
|
||||
gcry_free (buffer);
|
||||
buffer = NULL;
|
||||
if (!seqlist[seqlistidx].buffer)
|
||||
goto failure;
|
||||
seqlistidx++;
|
||||
}
|
||||
|
||||
if (kparms)
|
||||
{
|
||||
/* Encode the key. */
|
||||
buffer = build_key_sequence (kparms, &buflen);
|
||||
if (!buffer)
|
||||
goto failure;
|
||||
|
||||
/* Encrypt it. */
|
||||
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
|
||||
crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_3DES, 1);
|
||||
|
||||
/* Encode the encrypted stuff into a bag. */
|
||||
seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, &n);
|
||||
seqlist[seqlistidx].length = n;
|
||||
gcry_free (buffer);
|
||||
buffer = NULL;
|
||||
if (!seqlist[seqlistidx].buffer)
|
||||
goto failure;
|
||||
seqlistidx++;
|
||||
}
|
||||
|
||||
seqlist[seqlistidx].buffer = NULL;
|
||||
seqlist[seqlistidx].length = 0;
|
||||
|
||||
buffer = create_final (seqlist, &buflen);
|
||||
|
||||
failure:
|
||||
for ( ; seqlistidx; seqlistidx--)
|
||||
gcry_free (seqlist[seqlistidx].buffer);
|
||||
|
||||
*r_length = buffer? buflen : 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,8 +28,9 @@ gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length,
|
||||
void (*certcb)(void*, const unsigned char*, size_t),
|
||||
void *certcbarg);
|
||||
|
||||
unsigned char *p12_build (gcry_mpi_t *kparms, const char *pw,
|
||||
size_t *r_length);
|
||||
unsigned char *p12_build (gcry_mpi_t *kparms,
|
||||
unsigned char *cert, size_t certlen,
|
||||
const char *pw, size_t *r_length);
|
||||
|
||||
|
||||
#endif /*MINIP12_H*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* protect-tool.c - A tool to test the secret key protection
|
||||
* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -54,8 +54,10 @@ enum cmd_and_opt_values
|
||||
oP12Export,
|
||||
oStore,
|
||||
oForce,
|
||||
oHaveCert,
|
||||
oNoFailOnExist,
|
||||
oHomedir,
|
||||
oPrompt,
|
||||
|
||||
aTest };
|
||||
|
||||
@ -75,9 +77,12 @@ static int opt_armor;
|
||||
static int opt_store;
|
||||
static int opt_force;
|
||||
static int opt_no_fail_on_exist;
|
||||
static const char *passphrase;
|
||||
static int opt_have_cert;
|
||||
static const char *opt_passphrase;
|
||||
static char *opt_prompt;
|
||||
|
||||
static const char *get_passphrase (void);
|
||||
static char *get_passphrase (int promptno);
|
||||
static void release_passphrase (char *pw);
|
||||
static int store_private_key (const unsigned char *grip,
|
||||
const void *buffer, size_t length, int force);
|
||||
|
||||
@ -97,10 +102,12 @@ static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"},
|
||||
{ oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"},
|
||||
{ oHaveCert, "have-cert", 0, "certificate to export provided on STDIN"},
|
||||
{ oStore, "store", 0, "store the created key in the appropriate place"},
|
||||
{ oForce, "force", 0, "force overwriting"},
|
||||
{ oNoFailOnExist, "no-fail-on-exist", 0, "@" },
|
||||
{ oHomedir, "homedir", 2, "@" },
|
||||
{ oPrompt, "prompt", 2, "|ESCSTRING|use ESCSTRING as prompt in pinentry"},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -328,12 +335,15 @@ read_and_protect (const char *fname)
|
||||
unsigned char *key;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
char *pw;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_protect (key, get_passphrase (), &result, &resultlen);
|
||||
pw = get_passphrase (1);
|
||||
rc = agent_protect (key, pw, &result, &resultlen);
|
||||
release_passphrase (pw);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
@ -363,12 +373,14 @@ read_and_unprotect (const char *fname)
|
||||
unsigned char *key;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
char *pw;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_unprotect (key, get_passphrase (), &result, &resultlen);
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1)), &result, &resultlen);
|
||||
release_passphrase (pw);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
@ -632,6 +644,7 @@ import_p12_file (const char *fname)
|
||||
gcry_sexp_t s_key;
|
||||
unsigned char *key;
|
||||
unsigned char grip[20];
|
||||
char *pw;
|
||||
|
||||
/* fixme: we should release some stuff on error */
|
||||
|
||||
@ -639,8 +652,9 @@ import_p12_file (const char *fname)
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
kparms = p12_parse (buf, buflen, get_passphrase (),
|
||||
kparms = p12_parse (buf, buflen, (pw=get_passphrase (0)),
|
||||
import_p12_cert_cb, NULL);
|
||||
release_passphrase (pw);
|
||||
xfree (buf);
|
||||
if (!kparms)
|
||||
{
|
||||
@ -714,7 +728,8 @@ import_p12_file (const char *fname)
|
||||
gcry_sexp_release (s_key);
|
||||
|
||||
|
||||
rc = agent_protect (key, get_passphrase (), &result, &resultlen);
|
||||
rc = agent_protect (key, (pw=get_passphrase (0)), &result, &resultlen);
|
||||
release_passphrase (pw);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
@ -797,27 +812,113 @@ sexp_to_kparms (gcry_sexp_t sexp)
|
||||
}
|
||||
|
||||
|
||||
/* Check whether STRING is a KEYGRIP, i.e has the correct length and
|
||||
does only consist of uppercase hex characters. */
|
||||
static int
|
||||
is_keygrip (const char *string)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; string[i] && i < 41; i++)
|
||||
if (!strchr("01234567890ABCDEF", string[i]))
|
||||
return 0;
|
||||
return i == 40;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
export_p12_file (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
gcry_mpi_t kparms[9], *kp;
|
||||
unsigned char *key;
|
||||
size_t keylen;
|
||||
gcry_sexp_t private;
|
||||
struct rsa_secret_key_s sk;
|
||||
int i;
|
||||
unsigned char *cert = NULL;
|
||||
size_t certlen = 0;
|
||||
int keytype;
|
||||
size_t keylen_for_wipe = 0;
|
||||
char *pw;
|
||||
|
||||
if ( is_keygrip (fname) )
|
||||
{
|
||||
char hexgrip[40+4+1];
|
||||
char *p;
|
||||
|
||||
assert (strlen(fname) == 40);
|
||||
strcpy (stpcpy (hexgrip, fname), ".key");
|
||||
|
||||
p = make_filename (opt_homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
|
||||
key = read_key (p);
|
||||
xfree (p);
|
||||
}
|
||||
else
|
||||
key = read_key (fname);
|
||||
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
keytype = agent_private_key_type (key);
|
||||
if (keytype == PRIVATE_KEY_PROTECTED)
|
||||
{
|
||||
unsigned char *tmpkey;
|
||||
size_t tmplen;
|
||||
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1)), &tmpkey, &tmplen);
|
||||
release_passphrase (pw);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("unprotecting key `%s' failed: %s\n",
|
||||
fname, gpg_strerror (rc));
|
||||
xfree (key);
|
||||
return;
|
||||
}
|
||||
xfree (key);
|
||||
key = tmpkey;
|
||||
keylen_for_wipe = tmplen;
|
||||
|
||||
keytype = agent_private_key_type (key);
|
||||
}
|
||||
|
||||
if (keytype == PRIVATE_KEY_SHADOWED)
|
||||
{
|
||||
log_error ("`%s' is a shadowed private key - can't export it\n", fname);
|
||||
wipememory (key, keylen_for_wipe);
|
||||
xfree (key);
|
||||
return;
|
||||
}
|
||||
else if (keytype != PRIVATE_KEY_CLEAR)
|
||||
{
|
||||
log_error ("\%s' is not a private key\n", fname);
|
||||
wipememory (key, keylen_for_wipe);
|
||||
xfree (key);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (opt_have_cert)
|
||||
{
|
||||
cert = read_file ("-", &certlen);
|
||||
if (!cert)
|
||||
{
|
||||
wipememory (key, keylen_for_wipe);
|
||||
xfree (key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (gcry_sexp_new (&private, key, 0, 0))
|
||||
{
|
||||
log_error ("gcry_sexp_new failed\n");
|
||||
wipememory (key, keylen_for_wipe);
|
||||
xfree (key);
|
||||
xfree (cert);
|
||||
return;
|
||||
}
|
||||
wipememory (key, keylen_for_wipe);
|
||||
xfree (key);
|
||||
|
||||
kp = sexp_to_kparms (private);
|
||||
@ -825,6 +926,7 @@ export_p12_file (const char *fname)
|
||||
if (!kp)
|
||||
{
|
||||
log_error ("error converting key parameters\n");
|
||||
xfree (cert);
|
||||
return;
|
||||
}
|
||||
sk.n = kp[0];
|
||||
@ -850,7 +952,9 @@ export_p12_file (const char *fname)
|
||||
kparms[7] = sk.u;
|
||||
kparms[8] = NULL;
|
||||
|
||||
key = p12_build (kparms, get_passphrase (), &keylen);
|
||||
key = p12_build (kparms, cert, certlen, (pw=get_passphrase (0)), &keylen);
|
||||
release_passphrase (pw);
|
||||
xfree (cert);
|
||||
for (i=0; i < 8; i++)
|
||||
gcry_mpi_release (kparms[i]);
|
||||
if (!key)
|
||||
@ -861,6 +965,54 @@ export_p12_file (const char *fname)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Do the percent and plus/space unescaping in place and return the
|
||||
length of the valid buffer. */
|
||||
static size_t
|
||||
percent_plus_unescape (unsigned char *string)
|
||||
{
|
||||
unsigned char *p = string;
|
||||
size_t n = 0;
|
||||
|
||||
while (*string)
|
||||
{
|
||||
if (*string == '%' && string[1] && string[2])
|
||||
{
|
||||
string++;
|
||||
*p++ = xtoi_2 (string);
|
||||
n++;
|
||||
string+= 2;
|
||||
}
|
||||
else if (*string == '+')
|
||||
{
|
||||
*p++ = ' ';
|
||||
n++;
|
||||
string++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = *string++;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Remove percent and plus escaping and make sure that the reuslt is a
|
||||
string. This is done in place. Returns STRING. */
|
||||
static char *
|
||||
percent_plus_unescape_string (char *string)
|
||||
{
|
||||
unsigned char *p = string;
|
||||
size_t n;
|
||||
|
||||
n = percent_plus_unescape (p);
|
||||
p[n] = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
@ -918,10 +1070,12 @@ main (int argc, char **argv )
|
||||
case oP12Import: cmd = oP12Import; break;
|
||||
case oP12Export: cmd = oP12Export; break;
|
||||
|
||||
case oPassphrase: passphrase = pargs.r.ret_str; break;
|
||||
case oPassphrase: opt_passphrase = pargs.r.ret_str; break;
|
||||
case oStore: opt_store = 1; break;
|
||||
case oForce: opt_force = 1; break;
|
||||
case oNoFailOnExist: opt_no_fail_on_exist = 1; break;
|
||||
case oHaveCert: opt_have_cert = 1; break;
|
||||
case oPrompt: opt_prompt = pargs.r.ret_str; break;
|
||||
|
||||
default : pargs.err = 2; break;
|
||||
}
|
||||
@ -935,6 +1089,9 @@ main (int argc, char **argv )
|
||||
else if (argc > 1)
|
||||
usage (1);
|
||||
|
||||
if (opt_prompt)
|
||||
opt_prompt = percent_plus_unescape_string (xstrdup (opt_prompt));
|
||||
|
||||
if (cmd == oProtect)
|
||||
read_and_protect (fname);
|
||||
else if (cmd == oUnprotect)
|
||||
@ -965,21 +1122,27 @@ agent_exit (int rc)
|
||||
|
||||
|
||||
/* Return the passphrase string and ask the agent if it has not been
|
||||
set from the command line. */
|
||||
static const char *
|
||||
get_passphrase (void)
|
||||
set from the command line PROMPTNO select the prompt to display:
|
||||
0 = default
|
||||
1 = taken from the option --prompt
|
||||
*/
|
||||
static char *
|
||||
get_passphrase (int promptno)
|
||||
{
|
||||
char *pw;
|
||||
int err;
|
||||
const char *desc;
|
||||
|
||||
if (passphrase)
|
||||
return passphrase;
|
||||
if (opt_passphrase)
|
||||
return xstrdup (opt_passphrase);
|
||||
|
||||
pw = simple_pwquery (NULL,NULL,
|
||||
_("Enter passphrase:"),
|
||||
_("Please enter the passphrase or the PIN\n"
|
||||
"needed to complete this operation."),
|
||||
&err);
|
||||
if (promptno == 1 && opt_prompt)
|
||||
desc = opt_prompt;
|
||||
else
|
||||
desc = _("Please enter the passphrase or the PIN\n"
|
||||
"needed to complete this operation.");
|
||||
|
||||
pw = simple_pwquery (NULL,NULL, _("Passphrase:"), desc, &err);
|
||||
if (!pw)
|
||||
{
|
||||
if (err)
|
||||
@ -988,10 +1151,19 @@ get_passphrase (void)
|
||||
log_info ("cancelled\n");
|
||||
agent_exit (0);
|
||||
}
|
||||
passphrase = pw;
|
||||
return passphrase;
|
||||
|
||||
return pw;
|
||||
}
|
||||
|
||||
static void
|
||||
release_passphrase (char *pw)
|
||||
{
|
||||
if (pw)
|
||||
{
|
||||
wipememory (pw, strlen (pw));
|
||||
xfree (pw);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
store_private_key (const unsigned char *grip,
|
||||
|
@ -1,3 +1,7 @@
|
||||
2004-02-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* simple-pwquery.c (agent_open): Don't mangle INFOSTR.
|
||||
|
||||
2004-02-17 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO.
|
||||
|
@ -273,6 +273,11 @@ agent_open (int *rfd)
|
||||
#endif
|
||||
return SPWQ_NO_AGENT;
|
||||
}
|
||||
p = spwq_malloc (strlen (infostr)+1);
|
||||
if (!p)
|
||||
return SPWQ_OUT_OF_CORE;
|
||||
strcpy (p, infostr);
|
||||
infostr = p;
|
||||
|
||||
if ( !(p = strchr ( infostr, ':')) || p == infostr
|
||||
|| (p-infostr)+1 >= sizeof client_addr.sun_path )
|
||||
@ -344,7 +349,7 @@ agent_open (int *rfd)
|
||||
}
|
||||
|
||||
|
||||
/* Copy text to BUFFER and escape as required. Return a poiinter to
|
||||
/* Copy text to BUFFER and escape as required. Return a pointer to
|
||||
the end of the new buffer. NOte that BUFFER must be large enough
|
||||
to keep the entire text; allocataing it 3 times the size of TEXT
|
||||
is sufficient. */
|
||||
|
@ -152,6 +152,14 @@ Export all certificates stored in the Keybox or those specified by the
|
||||
optional @var{pattern}. When using along with the @code{--armor} option
|
||||
a few informational lines are prepended before each block.
|
||||
|
||||
@item --export-secret-key-p12 @var{key-id}
|
||||
@opindex export
|
||||
Export the private key and the certificate identified by @var{key-id}
|
||||
in a PKCS#12 format. When using along with the @code{--armor} option
|
||||
a few informational lines are prepended to the output. Note, that the
|
||||
PKCS#12 format is higly insecure and this command is only provided if
|
||||
there is no other way to exchange the private key.
|
||||
|
||||
@item --learn-card
|
||||
@opindex learn-card
|
||||
Read information about the private keys from the smartcard and import
|
||||
|
@ -127,7 +127,7 @@ open_card (CTRL ctrl, const char *apptype)
|
||||
}
|
||||
|
||||
|
||||
/* Do the percent and plus/space unescaping in place and return tghe
|
||||
/* Do the percent and plus/space unescaping in place and return the
|
||||
length of the valid buffer. */
|
||||
static size_t
|
||||
percent_plus_unescape (unsigned char *string)
|
||||
|
@ -1,3 +1,9 @@
|
||||
2004-02-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* export.c (export_p12, popen_protect_tool)
|
||||
(gpgsm_p12_export): New.
|
||||
* gpgsm.c (main): New command --export-secret-key-p12.
|
||||
|
||||
2004-02-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpgsm.c (set_debug): Set the new --debug-level flags.
|
||||
|
371
sm/export.c
371
sm/export.c
@ -1,5 +1,5 @@
|
||||
/* export.c
|
||||
* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -26,14 +26,29 @@
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "gpgsm.h"
|
||||
#include <gcrypt.h>
|
||||
#include <ksba.h>
|
||||
|
||||
#include "keydb.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#ifdef _POSIX_OPEN_MAX
|
||||
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
|
||||
#else
|
||||
#define MAX_OPEN_FDS 20
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static void print_short_info (ksba_cert_t cert, FILE *fp);
|
||||
static gpg_error_t export_p12 (const unsigned char *certimg, size_t certimglen,
|
||||
const char *prompt, const char *keygrip,
|
||||
FILE **retfp);
|
||||
|
||||
|
||||
|
||||
@ -195,6 +210,147 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
|
||||
}
|
||||
|
||||
|
||||
/* Export a certificates and its private key. */
|
||||
void
|
||||
gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
|
||||
{
|
||||
KEYDB_HANDLE hd;
|
||||
KEYDB_SEARCH_DESC *desc = NULL;
|
||||
Base64Context b64writer = NULL;
|
||||
ksba_writer_t writer;
|
||||
ksba_cert_t cert = NULL;
|
||||
int rc=0;
|
||||
const unsigned char *image;
|
||||
size_t imagelen;
|
||||
char *keygrip = NULL;
|
||||
char *prompt;
|
||||
char buffer[1024];
|
||||
int nread;
|
||||
FILE *datafp = NULL;
|
||||
|
||||
hd = keydb_new (0);
|
||||
if (!hd)
|
||||
{
|
||||
log_error ("keydb_new failed\n");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
desc = xtrycalloc (1, sizeof *desc);
|
||||
if (!desc)
|
||||
{
|
||||
log_error ("allocating memory for export failed: %s\n",
|
||||
gpg_strerror (OUT_OF_CORE (errno)));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = keydb_classify_name (name, desc);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("key `%s' not found: %s\n",
|
||||
name, gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Lookup the certificate an make sure that it is unique. */
|
||||
rc = keydb_search (hd, desc, 1);
|
||||
if (!rc)
|
||||
{
|
||||
rc = keydb_get_cert (hd, &cert);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = keydb_search (hd, desc, 1);
|
||||
if (!rc)
|
||||
rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
|
||||
else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
|
||||
rc = 0;
|
||||
if (rc)
|
||||
{
|
||||
log_error ("key `%s' not found: %s\n",
|
||||
name, gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
keygrip = gpgsm_get_keygrip_hexstring (cert);
|
||||
if (!keygrip || gpgsm_agent_havekey (keygrip))
|
||||
{
|
||||
/* Note, that the !keygrip case indicates a bad certificate. */
|
||||
rc = gpg_error (GPG_ERR_NO_SECKEY);
|
||||
log_error ("can't export key `%s': %s\n", name, gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
image = ksba_cert_get_image (cert, &imagelen);
|
||||
if (!image)
|
||||
{
|
||||
log_error ("ksba_cert_get_image failed\n");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (ctrl->create_pem)
|
||||
{
|
||||
print_short_info (cert, fp);
|
||||
putc ('\n', fp);
|
||||
}
|
||||
|
||||
ctrl->pem_name = "PKCS12";
|
||||
rc = gpgsm_create_writer (&b64writer, ctrl, fp, &writer);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't create writer: %s\n", gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
prompt = gpgsm_format_keydesc (cert);
|
||||
rc = export_p12 (image, imagelen, prompt, keygrip, &datafp);
|
||||
xfree (prompt);
|
||||
if (rc)
|
||||
goto leave;
|
||||
rewind (datafp);
|
||||
while ( (nread = fread (buffer, 1, sizeof buffer, datafp)) > 0 )
|
||||
if ((rc = ksba_writer_write (writer, buffer, nread)))
|
||||
{
|
||||
log_error ("write failed: %s\n", gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
if (ferror (datafp))
|
||||
{
|
||||
rc = gpg_error_from_errno (rc);
|
||||
log_error ("error reading temporary file: %s\n", gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (ctrl->create_pem)
|
||||
{
|
||||
/* We want one certificate per PEM block */
|
||||
rc = gpgsm_finish_writer (b64writer);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("write failed: %s\n", gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
gpgsm_destroy_writer (b64writer);
|
||||
b64writer = NULL;
|
||||
}
|
||||
|
||||
ksba_cert_release (cert);
|
||||
cert = NULL;
|
||||
|
||||
leave:
|
||||
if (datafp)
|
||||
fclose (datafp);
|
||||
gpgsm_destroy_writer (b64writer);
|
||||
ksba_cert_release (cert);
|
||||
xfree (desc);
|
||||
keydb_release (hd);
|
||||
}
|
||||
|
||||
|
||||
/* Print some info about the certifciate CERT to FP */
|
||||
static void
|
||||
print_short_info (ksba_cert_t cert, FILE *fp)
|
||||
@ -243,7 +399,220 @@ print_short_info (ksba_cert_t cert, FILE *fp)
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
popen_protect_tool (const char *pgmname,
|
||||
FILE *infile, FILE *outfile, FILE **statusfile,
|
||||
const char *prompt, const char *keygrip,
|
||||
pid_t *pid)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int fd, fdout, rp[2];
|
||||
int n, i;
|
||||
|
||||
fflush (infile);
|
||||
rewind (infile);
|
||||
fd = fileno (infile);
|
||||
fdout = fileno (outfile);
|
||||
if (fd == -1 || fdout == -1)
|
||||
log_fatal ("no file descriptor for temporary file: %s\n",
|
||||
strerror (errno));
|
||||
|
||||
/* Now start the protect-tool. */
|
||||
if (pipe (rp) == -1)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error (_("error creating a pipe: %s\n"), strerror (errno));
|
||||
return err;
|
||||
}
|
||||
|
||||
*pid = fork ();
|
||||
if (*pid == -1)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error (_("error forking process: %s\n"), strerror (errno));
|
||||
close (rp[0]);
|
||||
close (rp[1]);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!*pid)
|
||||
{ /* Child. */
|
||||
const char *arg0;
|
||||
|
||||
arg0 = strrchr (pgmname, '/');
|
||||
if (arg0)
|
||||
arg0++;
|
||||
else
|
||||
arg0 = pgmname;
|
||||
|
||||
/* Connect the infile to stdin. */
|
||||
if (fd != 0 && dup2 (fd, 0) == -1)
|
||||
log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
|
||||
|
||||
/* Connect the outfile to stdout. */
|
||||
if (fdout != 1 && dup2 (fdout, 1) == -1)
|
||||
log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
|
||||
|
||||
/* Connect stderr to our pipe. */
|
||||
if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
|
||||
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
|
||||
|
||||
/* Close all other files. */
|
||||
n = sysconf (_SC_OPEN_MAX);
|
||||
if (n < 0)
|
||||
n = MAX_OPEN_FDS;
|
||||
for (i=3; i < n; i++)
|
||||
close(i);
|
||||
errno = 0;
|
||||
|
||||
execlp (pgmname, arg0,
|
||||
"--homedir", opt.homedir,
|
||||
"--p12-export",
|
||||
"--prompt", prompt?prompt:"",
|
||||
"--",
|
||||
keygrip,
|
||||
NULL);
|
||||
/* No way to print anything, as we have closed all streams. */
|
||||
_exit (31);
|
||||
}
|
||||
|
||||
/* Parent. */
|
||||
close (rp[1]);
|
||||
*statusfile = fdopen (rp[0], "r");
|
||||
if (!*statusfile)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error ("can't fdopen pipe for reading: %s", strerror (errno));
|
||||
kill (*pid, SIGTERM);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
export_p12 (const unsigned char *certimg, size_t certimglen,
|
||||
const char *prompt, const char *keygrip,
|
||||
FILE **retfp)
|
||||
{
|
||||
const char *pgmname;
|
||||
gpg_error_t err = 0, child_err = 0;
|
||||
int i, c, cont_line;
|
||||
unsigned int pos;
|
||||
FILE *infp = NULL, *outfp = NULL, *fp = NULL;
|
||||
char buffer[1024];
|
||||
pid_t pid = -1;
|
||||
|
||||
if (!opt.protect_tool_program || !*opt.protect_tool_program)
|
||||
pgmname = GNUPG_DEFAULT_PROTECT_TOOL;
|
||||
else
|
||||
pgmname = opt.protect_tool_program;
|
||||
|
||||
infp = tmpfile ();
|
||||
if (!infp)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error (_("error creating temporary file: %s\n"), strerror (errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (fwrite (certimg, certimglen, 1, infp) != 1)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error (_("error writing to temporary file: %s\n"),
|
||||
strerror (errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
outfp = tmpfile ();
|
||||
if (!outfp)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error (_("error creating temporary file: %s\n"), strerror (errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = popen_protect_tool (pgmname, infp, outfp, &fp, prompt, keygrip, &pid);
|
||||
if (err)
|
||||
{
|
||||
pid = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
fclose (infp);
|
||||
infp = NULL;
|
||||
|
||||
/* Read stderr of the protect tool. */
|
||||
pos = 0;
|
||||
cont_line = 0;
|
||||
while ((c=getc (fp)) != EOF)
|
||||
{
|
||||
/* fixme: We could here grep for status information of the
|
||||
protect tool to figure out better error codes for
|
||||
CHILD_ERR. */
|
||||
buffer[pos++] = c;
|
||||
if (pos >= 5 /*sizeof buffer - 1*/ || c == '\n')
|
||||
{
|
||||
buffer[pos - (c == '\n')] = 0;
|
||||
if (cont_line)
|
||||
log_printf ("%s", buffer);
|
||||
else
|
||||
log_info ("%s", buffer);
|
||||
pos = 0;
|
||||
cont_line = (c != '\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (pos)
|
||||
{
|
||||
buffer[pos] = 0;
|
||||
if (cont_line)
|
||||
log_printf ("%s\n", buffer);
|
||||
else
|
||||
log_info ("%s\n", buffer);
|
||||
}
|
||||
else if (cont_line)
|
||||
log_printf ("\n");
|
||||
|
||||
/* If we found no error in the output of the child, setup a suitable
|
||||
error code, which will later be reset if the exit status of the
|
||||
child is 0. */
|
||||
if (!child_err)
|
||||
child_err = gpg_error (GPG_ERR_DECRYPT_FAILED);
|
||||
|
||||
cleanup:
|
||||
if (infp)
|
||||
fclose (infp);
|
||||
if (fp)
|
||||
fclose (fp);
|
||||
if (pid != -1)
|
||||
{
|
||||
int status;
|
||||
|
||||
while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
if (i == -1)
|
||||
log_error (_("waiting for protect-tools to terminate failed: %s\n"),
|
||||
strerror (errno));
|
||||
else if (WIFEXITED (status) && WEXITSTATUS (status) == 31)
|
||||
log_error (_("error running `%s': probably not installed\n"), pgmname);
|
||||
else if (WIFEXITED (status) && WEXITSTATUS (status))
|
||||
log_error (_("error running `%s': exit status %d\n"), pgmname,
|
||||
WEXITSTATUS (status));
|
||||
else if (!WIFEXITED (status))
|
||||
log_error (_("error running `%s': terminated\n"), pgmname);
|
||||
else
|
||||
child_err = 0;
|
||||
}
|
||||
if (!err)
|
||||
err = child_err;
|
||||
if (err)
|
||||
{
|
||||
if (outfp)
|
||||
fclose (outfp);
|
||||
}
|
||||
else
|
||||
*retfp = outfp;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
10
sm/gpgsm.c
10
sm/gpgsm.c
@ -74,6 +74,7 @@ enum cmd_and_opt_values {
|
||||
aSendKeys,
|
||||
aRecvKeys,
|
||||
aExport,
|
||||
aExportSecretKeyP12,
|
||||
aCheckKeys, /* nyi */
|
||||
aServer,
|
||||
aLearnCard,
|
||||
@ -344,6 +345,8 @@ static ARGPARSE_OPTS opts[] = {
|
||||
#endif
|
||||
{ aDummy, "throw-keyid", 0, "@"},
|
||||
{ aDummy, "notation-data", 2, "@"},
|
||||
{ aExportSecretKeyP12, "export-secret-key-p12", 256, "@"},
|
||||
|
||||
|
||||
{ 302, NULL, 0, N_(
|
||||
"@\n(See the man page for a complete listing of all commands and options)\n"
|
||||
@ -869,6 +872,7 @@ main ( int argc, char **argv)
|
||||
case aSendKeys:
|
||||
case aRecvKeys:
|
||||
case aExport:
|
||||
case aExportSecretKeyP12:
|
||||
case aListKeys:
|
||||
case aListExternalKeys:
|
||||
case aListSecretKeys:
|
||||
@ -1406,6 +1410,12 @@ main ( int argc, char **argv)
|
||||
free_strlist(sl);
|
||||
break;
|
||||
|
||||
case aExportSecretKeyP12:
|
||||
if (argc == 1)
|
||||
gpgsm_p12_export (&ctrl, *argv, stdout);
|
||||
else
|
||||
wrong_args (_("--export-secret-key-p12 KEY-ID"));
|
||||
break;
|
||||
|
||||
case aSendKeys:
|
||||
case aRecvKeys:
|
||||
|
@ -247,6 +247,7 @@ int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
|
||||
|
||||
/*-- export.c --*/
|
||||
void gpgsm_export (ctrl_t ctrl, STRLIST names, FILE *fp);
|
||||
void gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp);
|
||||
|
||||
/*-- delete.c --*/
|
||||
int gpgsm_delete (ctrl_t ctrl, STRLIST names);
|
||||
|
@ -562,7 +562,7 @@ parse_p12 (ksba_reader_t reader, FILE **retfp)
|
||||
}
|
||||
while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
|
||||
{
|
||||
if (fwrite (buffer, nread, 1, tmpfp) != 1)
|
||||
if (nread && fwrite (buffer, nread, 1, tmpfp) != 1)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
log_error (_("error writing to temporary file: %s\n"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user