mirror of
https://github.com/QuasarApp/qca.git
synced 2025-04-28 12:34:31 +00:00
basic cms sign/encrypt capability, added smime functions to qcatool
svn path=/trunk/kdesupport/qca/; revision=412156
This commit is contained in:
parent
020d9a2db3
commit
4ea4093794
@ -785,13 +785,14 @@ static QByteArray get_cert_subject_key_id(X509_EXTENSION *ex)
|
||||
return out;
|
||||
}
|
||||
|
||||
static QByteArray get_cert_issuer_key_id(X509_EXTENSION *ex)
|
||||
// TODO: removed because it was crashing on the qualityssl intermediate ca cert
|
||||
/*static QByteArray get_cert_issuer_key_id(X509_EXTENSION *ex)
|
||||
{
|
||||
AUTHORITY_KEYID *akid = (AUTHORITY_KEYID *)X509V3_EXT_d2i(ex);
|
||||
QByteArray out((const char *)ASN1_STRING_data(akid->keyid), ASN1_STRING_length(akid->keyid));
|
||||
AUTHORITY_KEYID_free(akid);
|
||||
return out;
|
||||
}
|
||||
}*/
|
||||
|
||||
static QCA::Validity convert_verify_error(int err)
|
||||
{
|
||||
@ -2962,13 +2963,14 @@ public:
|
||||
p.subjectId += get_cert_subject_key_id(ex);
|
||||
}
|
||||
|
||||
pos = X509_get_ext_by_NID(x, NID_authority_key_identifier, -1);
|
||||
// TODO:
|
||||
/*pos = X509_get_ext_by_NID(x, NID_authority_key_identifier, -1);
|
||||
if(pos != -1)
|
||||
{
|
||||
X509_EXTENSION *ex = X509_get_ext(x, pos);
|
||||
if(ex)
|
||||
p.issuerId += get_cert_issuer_key_id(ex);
|
||||
}
|
||||
}*/
|
||||
|
||||
_props = p;
|
||||
//printf("[%p] made props: [%s]\n", this, _props.subject[QCA::CommonName].toLatin1().data());
|
||||
@ -3877,6 +3879,302 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/*char *mime_enveloped =
|
||||
"From %1 Sat May 7 22:18:36 2005\r\n"
|
||||
"Mime-Version: 1.0\r\n"
|
||||
"Content-Type: application/pkcs7-mime;\r\n"
|
||||
" name=smime.p7m;\r\n"
|
||||
" smime-type=enveloped-data\r\n"
|
||||
//"Message-Id: <08F8A1B4-04A3-4B6D-AF21-3AF7FD15EFE4@uci.edu>\r\n"
|
||||
"Content-Disposition: attachment;\r\n"
|
||||
" filename=smime.p7m\r\n"
|
||||
"Content-Transfer-Encoding: base64\r\n"
|
||||
"From: %2\r\n"
|
||||
"Subject: %3\r\n"
|
||||
"Date: Sat, 7 May 2005 22:18:36 -0700\r\n"
|
||||
"To: %4\r\n"
|
||||
"\r\n"
|
||||
"%5\r\n";*/
|
||||
|
||||
class CMSContext : public QCA::SMSContext
|
||||
{
|
||||
public:
|
||||
CMSContext(QCA::Provider *p) : QCA::SMSContext(p, "cms")
|
||||
{
|
||||
}
|
||||
|
||||
~CMSContext()
|
||||
{
|
||||
}
|
||||
|
||||
virtual Context *clone() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void setTrustedCertificates(const QCA::CertificateCollection &trusted)
|
||||
{
|
||||
// TODO
|
||||
Q_UNUSED(trusted);
|
||||
}
|
||||
|
||||
virtual void setPrivateKeys(const QList<QCA::PrivateKey> &keys)
|
||||
{
|
||||
// TODO
|
||||
Q_UNUSED(keys);
|
||||
}
|
||||
|
||||
virtual QCA::MessageContext *createMessage() const;
|
||||
};
|
||||
|
||||
class MyMessageContext : public QCA::MessageContext
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QCA::SecureMessageKey signer;
|
||||
QCA::SecureMessageKeyList to;
|
||||
QCA::SecureMessage::SignMode signMode;
|
||||
bool bundleSigner;
|
||||
bool smime;
|
||||
QCA::SecureMessage::Format format;
|
||||
|
||||
Operation op;
|
||||
|
||||
QSecureArray in, out;
|
||||
QSecureArray sig;
|
||||
|
||||
MyMessageContext(QCA::Provider *p) : QCA::MessageContext(p, "cmsmsg")
|
||||
{
|
||||
}
|
||||
|
||||
~MyMessageContext()
|
||||
{
|
||||
}
|
||||
|
||||
virtual Context *clone() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool canSignMultiple() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual QCA::SecureMessage::Type type() const
|
||||
{
|
||||
return QCA::SecureMessage::CMS;
|
||||
}
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setupEncrypt(const QCA::SecureMessageKeyList &keys)
|
||||
{
|
||||
to = keys;
|
||||
}
|
||||
|
||||
virtual void setupSign(const QCA::SecureMessageKeyList &keys, QCA::SecureMessage::SignMode m, bool bundleSigner, bool smime)
|
||||
{
|
||||
signer = keys.first();
|
||||
signMode = m;
|
||||
this->bundleSigner = bundleSigner;
|
||||
this->smime = smime;
|
||||
}
|
||||
|
||||
virtual void setupVerify(const QSecureArray &detachedSig)
|
||||
{
|
||||
// TODO
|
||||
Q_UNUSED(detachedSig);
|
||||
}
|
||||
|
||||
virtual void start(QCA::SecureMessage::Format f, Operation op)
|
||||
{
|
||||
format = f;
|
||||
|
||||
// TODO: other operations
|
||||
if(op == Sign)
|
||||
{
|
||||
this->op = op;
|
||||
}
|
||||
else if(op == Encrypt)
|
||||
{
|
||||
this->op = op;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void update(const QSecureArray &in)
|
||||
{
|
||||
this->in.append(in);
|
||||
}
|
||||
|
||||
virtual QSecureArray read()
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
virtual void end()
|
||||
{
|
||||
// sign
|
||||
if(op == Sign)
|
||||
{
|
||||
QCA::CertificateChain chain = signer.x509CertificateChain();
|
||||
QCA::Certificate cert = chain.primary();
|
||||
QList<QCA::Certificate> nonroots;
|
||||
if(chain.count() > 1)
|
||||
{
|
||||
for(int n = 1; n < chain.count(); ++n)
|
||||
nonroots.append(chain[n]);
|
||||
}
|
||||
QCA::PrivateKey key = signer.x509PrivateKey();
|
||||
|
||||
MyCertContext *cc = static_cast<MyCertContext *>(cert.context());
|
||||
MyPKeyContext *kc = static_cast<MyPKeyContext *>(key.context());
|
||||
|
||||
X509 *cx = cc->item.cert;
|
||||
EVP_PKEY *kx = kc->get_pkey();
|
||||
|
||||
STACK_OF(X509) *other_certs;
|
||||
BIO *bi;
|
||||
int flags;
|
||||
PKCS7 *p7;
|
||||
|
||||
// nonroots
|
||||
other_certs = sk_X509_new_null();
|
||||
for(int n = 0; n < nonroots.count(); ++n)
|
||||
{
|
||||
X509 *x = static_cast<MyCertContext *>(nonroots[n].context())->item.cert;
|
||||
CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
|
||||
sk_X509_push(other_certs, x);
|
||||
}
|
||||
|
||||
bi = BIO_new(BIO_s_mem());
|
||||
BIO_write(bi, in.data(), in.size());
|
||||
|
||||
flags = 0;
|
||||
flags |= PKCS7_BINARY;
|
||||
flags |= PKCS7_DETACHED; // FIXME: signmode
|
||||
//flags |= PKCS7_NOCERTS; // FIXME: bundleSigner
|
||||
p7 = PKCS7_sign(cx, kx, other_certs, bi, flags);
|
||||
|
||||
BIO_free(bi);
|
||||
sk_X509_pop_free(other_certs, X509_free);
|
||||
|
||||
if(p7)
|
||||
{
|
||||
//printf("good\n");
|
||||
BIO *bo;
|
||||
|
||||
//BIO *bo = BIO_new(BIO_s_mem());
|
||||
//i2d_PKCS7_bio(bo, p7);
|
||||
//PEM_write_bio_PKCS7(bo, p7);
|
||||
//QSecureArray buf = bio2buf(bo);
|
||||
//printf("[%s]\n", buf.data());
|
||||
|
||||
// FIXME: format
|
||||
bo = BIO_new(BIO_s_mem());
|
||||
i2d_PKCS7_bio(bo, p7);
|
||||
sig = bio2buf(bo);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("bad\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(op == Encrypt)
|
||||
{
|
||||
// TODO: support multiple recipients
|
||||
QCA::Certificate target = to.first().x509CertificateChain().primary();
|
||||
|
||||
STACK_OF(X509) *other_certs;
|
||||
BIO *bi;
|
||||
int flags;
|
||||
PKCS7 *p7;
|
||||
|
||||
other_certs = sk_X509_new_null();
|
||||
X509 *x = static_cast<MyCertContext *>(target.context())->item.cert;
|
||||
CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
|
||||
sk_X509_push(other_certs, x);
|
||||
|
||||
bi = BIO_new(BIO_s_mem());
|
||||
BIO_write(bi, in.data(), in.size());
|
||||
|
||||
flags = 0;
|
||||
flags |= PKCS7_BINARY;
|
||||
p7 = PKCS7_encrypt(other_certs, bi, EVP_des_ede3_cbc(), flags); // TODO: cipher?
|
||||
|
||||
BIO_free(bi);
|
||||
sk_X509_pop_free(other_certs, X509_free);
|
||||
|
||||
QString env;
|
||||
if(p7)
|
||||
{
|
||||
// FIXME: format
|
||||
BIO *bo = BIO_new(BIO_s_mem());
|
||||
i2d_PKCS7_bio(bo, p7);
|
||||
//PEM_write_bio_PKCS7(bo, p7);
|
||||
out = bio2buf(bo);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("bad\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool finished() const
|
||||
{
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void waitForFinished(int msecs)
|
||||
{
|
||||
// TODO
|
||||
Q_UNUSED(msecs);
|
||||
}
|
||||
|
||||
virtual bool success() const
|
||||
{
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual QCA::SecureMessage::Error errorCode() const
|
||||
{
|
||||
// TODO
|
||||
return QCA::SecureMessage::ErrorUnknown;
|
||||
}
|
||||
|
||||
virtual QSecureArray signature() const
|
||||
{
|
||||
return sig;
|
||||
}
|
||||
|
||||
virtual QString hashName() const
|
||||
{
|
||||
return "sha1";
|
||||
}
|
||||
|
||||
virtual QCA::SecureMessageSignatureList signers() const
|
||||
{
|
||||
// TODO
|
||||
return QCA::SecureMessageSignatureList();
|
||||
}
|
||||
};
|
||||
|
||||
QCA::MessageContext *CMSContext::createMessage() const
|
||||
{
|
||||
return new MyMessageContext(provider());
|
||||
}
|
||||
|
||||
|
||||
class opensslCipherContext : public QCA::CipherContext
|
||||
{
|
||||
@ -4073,6 +4371,7 @@ public:
|
||||
list += "csr";
|
||||
list += "crl";
|
||||
list += "tls";
|
||||
list += "cms";
|
||||
|
||||
return list;
|
||||
}
|
||||
@ -4170,6 +4469,8 @@ public:
|
||||
return new MyCRLContext( this );
|
||||
else if ( type == "tls" )
|
||||
return new MyTLSContext( this );
|
||||
else if ( type == "cms" )
|
||||
return new CMSContext( this );
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
@ -268,6 +268,62 @@ static QString validityToString(QCA::Validity v)
|
||||
return s;
|
||||
}
|
||||
|
||||
char *mime_signpart =
|
||||
"Content-Type: text/plain; charset=ISO-8859-1\r\n"
|
||||
"Content-Transfer-Encoding: 7bit\r\n"
|
||||
"\r\n"
|
||||
"%1";
|
||||
|
||||
char *mime_signed =
|
||||
"Content-Type: multipart/signed;\r\n"
|
||||
" micalg=%1;\r\n"
|
||||
" boundary=QCATOOL-0001;\r\n"
|
||||
" protocol=\"application/pkcs7-signature\"\r\n"
|
||||
"\r\n"
|
||||
"\r\n"
|
||||
"--QCATOOL-0001\r\n"
|
||||
"%2\r\n"
|
||||
"--QCATOOL-0001\r\n"
|
||||
"Content-Transfer-Encoding: base64\r\n"
|
||||
"Content-Type: application/pkcs7-signature;\r\n"
|
||||
" name=smime.p7s\r\n"
|
||||
"Content-Disposition: attachment;\r\n"
|
||||
" filename=smime.p7s\r\n"
|
||||
"\r\n"
|
||||
"%3\r\n"
|
||||
"\r\n"
|
||||
"--QCATOOL-0001--\r\n";
|
||||
|
||||
char *mime_enveloped =
|
||||
"Mime-Version: 1.0\r\n"
|
||||
"Content-Transfer-Encoding: base64\r\n"
|
||||
"Content-Type: application/pkcs7-mime;\r\n"
|
||||
" name=smime.p7m;\r\n"
|
||||
" smime-type=enveloped-data\r\n"
|
||||
"Content-Disposition: attachment;\r\n"
|
||||
" filename=smime.p7m\r\n"
|
||||
"\r\n"
|
||||
"%1\r\n";
|
||||
|
||||
static QString add_cr(const QString &in)
|
||||
{
|
||||
QString out = in;
|
||||
int at = 0;
|
||||
while(1)
|
||||
{
|
||||
at = out.indexOf('\n', at);
|
||||
if(at == -1)
|
||||
break;
|
||||
if(at - 1 >= 0 && out[at - 1] != '\r')
|
||||
{
|
||||
out.insert(at, '\r');
|
||||
++at;
|
||||
}
|
||||
++at;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void usage()
|
||||
{
|
||||
printf("qcatool: simple qca testing tool\n");
|
||||
@ -291,6 +347,9 @@ static void usage()
|
||||
printf(" --showreq [certreq.pem]\n");
|
||||
printf(" --validate [cert.pem] (nonroots.pem)\n");
|
||||
printf("\n");
|
||||
printf(" --smime sign [priv.pem] [messagefile] [cert.pem] [nonroots.pem] (passphrase)\n");
|
||||
printf(" --smime encrypt [cert.pem] [messagefile]\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -1007,6 +1066,174 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if(args[0] == "--smime")
|
||||
{
|
||||
if(args.count() < 2)
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(args[1] == "sign")
|
||||
{
|
||||
if(args.count() < 6)
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
QSecureArray passphrase;
|
||||
if(args.count() >= 7)
|
||||
passphrase = args[6].toLatin1();
|
||||
|
||||
QCA::PrivateKey key;
|
||||
if(!passphrase.isEmpty())
|
||||
key = QCA::PrivateKey(args[2], passphrase);
|
||||
else
|
||||
key = QCA::PrivateKey(args[2]);
|
||||
if(key.isNull())
|
||||
{
|
||||
printf("Error reading key file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::Certificate cert(args[4]);
|
||||
if(cert.isNull())
|
||||
{
|
||||
printf("Error reading cert file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::CertificateCollection nonroots = QCA::CertificateCollection::fromFlatTextFile(args[5]);
|
||||
|
||||
QFile infile(args[3]);
|
||||
if(!infile.open(QFile::ReadOnly))
|
||||
{
|
||||
printf("Error opening message file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::SecureMessageKey skey;
|
||||
{
|
||||
QCA::CertificateChain chain;
|
||||
chain += cert;
|
||||
chain += nonroots.certificates();
|
||||
skey.setX509CertificateChain(chain);
|
||||
skey.setX509PrivateKey(key);
|
||||
}
|
||||
|
||||
QString text = add_cr(QString::fromLatin1(infile.readAll()));
|
||||
QByteArray plain = QString(mime_signpart).arg(text).toLatin1();
|
||||
|
||||
QCA::CMS cms;
|
||||
QCA::SecureMessage msg(&cms);
|
||||
msg.setSigner(skey);
|
||||
msg.startSign(QCA::SecureMessage::Detached);
|
||||
msg.update(plain);
|
||||
msg.end();
|
||||
msg.waitForFinished(-1);
|
||||
|
||||
if(!msg.success())
|
||||
{
|
||||
printf("Error signing: [%d]\n", msg.errorCode());
|
||||
return 1;
|
||||
}
|
||||
|
||||
QFileInfo fi(infile.fileName());
|
||||
|
||||
QFile outfile(fi.baseName() + "_signed.txt");
|
||||
if(!outfile.open(QFile::WriteOnly | QFile::Truncate))
|
||||
{
|
||||
printf("Error opening sig file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QSecureArray sig = msg.signature();
|
||||
|
||||
QCA::Base64 enc;
|
||||
enc.setLineBreaksEnabled(true);
|
||||
enc.setLineBreaksColumn(76);
|
||||
QString sigtext = add_cr(enc.arrayToString(sig));
|
||||
|
||||
QString str = QString(mime_signed).arg(msg.hashName()).arg(QString(plain)).arg(sigtext);
|
||||
QTextStream ts(&outfile);
|
||||
ts << str;
|
||||
|
||||
printf("Wrote %s\n", qPrintable(outfile.fileName()));
|
||||
}
|
||||
else if(args[1] == "encrypt")
|
||||
{
|
||||
if(args.count() < 4)
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::Certificate cert(args[2]);
|
||||
if(cert.isNull())
|
||||
{
|
||||
printf("Error reading cert file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QFile infile(args[3]);
|
||||
if(!infile.open(QFile::ReadOnly))
|
||||
{
|
||||
printf("Error opening message file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::SecureMessageKey skey;
|
||||
{
|
||||
QCA::CertificateChain chain;
|
||||
chain += cert;
|
||||
skey.setX509CertificateChain(chain);
|
||||
}
|
||||
|
||||
QByteArray plain = infile.readAll();
|
||||
|
||||
QCA::CMS cms;
|
||||
QCA::SecureMessage msg(&cms);
|
||||
msg.setRecipient(skey);
|
||||
msg.startEncrypt();
|
||||
msg.update(plain);
|
||||
msg.end();
|
||||
msg.waitForFinished(-1);
|
||||
|
||||
if(!msg.success())
|
||||
{
|
||||
printf("Error encrypting: [%d]\n", msg.errorCode());
|
||||
return 1;
|
||||
}
|
||||
|
||||
QFileInfo fi(infile.fileName());
|
||||
|
||||
QFile outfile(fi.baseName() + "_encrypted.txt");
|
||||
if(!outfile.open(QFile::WriteOnly | QFile::Truncate))
|
||||
{
|
||||
printf("Error opening output file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QSecureArray result = msg.read();
|
||||
|
||||
QCA::Base64 enc;
|
||||
enc.setLineBreaksEnabled(true);
|
||||
enc.setLineBreaksColumn(76);
|
||||
QString enctext = add_cr(enc.arrayToString(result));
|
||||
|
||||
QString str = QString(mime_enveloped).arg(enctext);
|
||||
QTextStream ts(&outfile);
|
||||
ts << str;
|
||||
|
||||
printf("Wrote %s\n", qPrintable(outfile.fileName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usage();
|
||||
|
Loading…
x
Reference in New Issue
Block a user