According to rfc5208, https://www.rfc-editor.org/rfc/rfc5208#page-3 , pkcs8 keys can have optional attributes.
How can I generate an RSA key with these optional attributes? I have only seen the option for openssl genpkey pkeyopt, which i can specify things like rsa_keygen_bits:2048 to set the keysize, but if I wanted to do sometihng like add an optional attribute for a name associated with that private key, how would I do that?
You can't do it with commandline, which may be fortunate as that wouldn't be a programming problem and thus not really ontopic for SO. So here's a minimal programmed example:
/* SO58667890 19nov04 PKCS8 attribute */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
void err (const char * s, int n){ fprintf(stderr, "%s:%d\n", s,n); ERR_print_errors_fp (stderr); exit(1); }
int main (int argc, char** argv){
if( argc < 4 ){ fprintf(stderr, "Usage: %s privkey.pem attrname attrvalue\n"); return 0; }
int rv;
BIO *bio = BIO_new_file (argv[1],"r");
if( !bio ) err("BIO_new_file-r",0);
EVP_PKEY *pkey = PEM_read_bio_PrivateKey (bio, NULL, NULL,NULL);
if( !pkey ) err("PEM_read_bio_PrivateKey",0);
PKCS8_PRIV_KEY_INFO *p8inf = EVP_PKEY2PKCS8 (pkey);
if( !p8inf ) err("EVP_PKEY2PKCS8",0);
int nid = OBJ_txt2nid (argv[2]);
if( nid == NID_undef ) err("OBJ_txt2nid",nid);
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
rv = PKCS8_pkey_add1_attr_by_NID (p8inf, nid, V_ASN1_IA5STRING, (unsigned char*)argv[3], strlen(argv[3]));
if( !rv ) err("PKCS8_pkey_add1_attr_by_NID",rv);
#else
if( !X509at_add1_attr_by_NID (&p8inf->attributes, nid, V_ASN1_IA5STRING, (unsigned char*)argv[3], strlen(argv[3])) )
err("X509at_add1_attr_by_NID",0);
#endif
bio = BIO_new_file (argv[1],"w");
if( !bio ) err("BIO_new_file-w",0);
rv = PEM_write_bio_PKCS8_PRIV_KEY_INFO (bio, p8inf);
if( !rv ) err("PEM_write_bio_PKCS8info",rv);
rv = BIO_free(bio);
if( !rv ) err("BIO_free",rv);
return 0;
}
However, as Maarten commented, these attributes are very rarely used. I've seen at least one case here where Java didn't implement them at all. In the PKI world, including S/MIME and SSL/TLS, you usually have an X.509 (or PKIX) certificate for the publickey matching your privatekey, and the certificate has lots of useful information about the identity of the owner and authorized usage of the key -- and this information is in a form programs largely understand and can use (although there are now and then issues with one party or system creating a new certificate extension that isn't understood by another party or system). For example, when you look in Windows certmgr or MacOS keychain, you don't see a privatekey as such; you see a certificate with a privatekey attached to it.
Related
I am new to OpenSSL and is currently working on a C++ project that has a dependency on OpenSSL. Recently i started using a machine that had openSSL 3.0 installed but is unable to build the project because i get compiler errors like below ( please note that warnings are treated as error in the project)
error: 'MD5' is deprecated
if (!MD5((uint8_t*)key.data(), key.size(), hashOutput)) {
^
/usr/local/Cellar/openssl#3/3.0.0_1/include/openssl/md5.h:52:1: note: 'MD5' has been explicitly marked deprecated here
OSSL_DEPRECATEDIN_3_0 unsigned char *MD5(const unsigned char *d, size_t n,
^
/usr/local/Cellar/openssl#3/3.0.0_1/include/openssl/macros.h:182:49: note: expanded from macro 'OSSL_DEPRECATEDIN_3_0'
# define OSSL_DEPRECATEDIN_3_0 OSSL_DEPRECATED(3.0)
^
/usr/local/Cellar/openssl#3/3.0.0_1/include/openssl/macros.h:62:52: note: expanded from macro 'OSSL_DEPRECATED'
# define OSSL_DEPRECATED(since) __attribute__((deprecated))
^
1 error generated.
Doing some investigation i found that these API's were deprecated in OpenSSL 3.0.0 and now i have three options
Ignore the warnings. They are just warnings. The deprecated functions are still present and you may still use them. However be aware that they may be removed from a future version of OpenSSL.
Suppress the warnings. Refer to your compiler documentation on how to do this.
Remove your usage of the low level APIs. In this case you will need to rewrite your code to use the high level APIs instead.
I choose the third option but even after reading documentations related to OpenSSL, i am still not sure what is the EVP API equivalent of the MD5 method which got deprecated in OpenSSL 3.0.0. If you check the link https://www.openssl.org/docs/manmaster/man3/MD5.html it states that
All of the functions described on this page are deprecated.
Applications should instead use EVP_DigestInit_ex(3),
EVP_DigestUpdate(3) and EVP_DigestFinal_ex(3).
Would be great if someone can provide me some feedback/help or additional resources/links that might help me understand using the high Level OpenSSL API's in general so that i can make it work for my use case i.e MD5 encryption.
I must also mention that i downloaded OpenSSL repo from github and found that the implementation of md5 method is as below
unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)
{
MD5_CTX c;
static unsigned char m[MD5_DIGEST_LENGTH];
if (md == NULL)
md = m;
if (!MD5_Init(&c))
return NULL;
#ifndef CHARSET_EBCDIC
MD5_Update(&c, d, n);
#else
{
char temp[1024];
unsigned long chunk;
while (n > 0) {
chunk = (n > sizeof(temp)) ? sizeof(temp) : n;
ebcdic2ascii(temp, d, chunk);
MD5_Update(&c, temp, chunk);
n -= chunk;
d += chunk;
}
}
#endif
MD5_Final(md, &c);
OPENSSL_cleanse(&c, sizeof(c)); /* security consideration */
return md;
}
I hope this example would help (I'm not checking for errors and exceptions in my example, so be aware of that):
Deprecated Implementation
#include <openssl/md5.h>
static void calculate_md5(unsigned char* buf, unsigned int buf_size)
{
unsigned char md5_digest[MD5_DIGEST_LENGTH];
MD5_CTX md5ctx;
MD5_Init(&md5ctx);
MD5_Update(&md5ctx, buf, buf_size);
MD5_Final(md5_digest, &md5ctx);
}
New Implementation:
#include <openssl/evp.h>
static void calculate_md5(unsigned char* buf, unsigned int buf_size)
{
EVP_MD_CTX *mdctx;
unsigned char *md5_digest;
unsigned int md5_digest_len = EVP_MD_size(EVP_md5());
// MD5_Init
mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(mdctx, EVP_md5(), NULL);
// MD5_Update
EVP_DigestUpdate(mdctx, buf, buf_size);
// MD5_Final
md5_digest = (unsigned char *)OPENSSL_malloc(md5_digest_len);
EVP_DigestFinal_ex(mdctx, md5_digest, &md5_digest_len);
EVP_MD_CTX_free(mdctx);
}
I tried to implement a "very" simple encryption/decryption example. I need it for a project where I would like to encrypt some user information. I can't encrypt the whole database but only some fields in a table.
The database and most of the rest of the project works, except the encryption:
Here is a simplified version of it:
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
/* ckey and ivec are the two 128-bits keys necessary to
en- and recrypt your data. Note that ckey can be
192 or 256 bits as well
*/
unsigned char ckey[] = "helloworldkey";
unsigned char ivec[] = "goodbyworldkey";
int bytes_read;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char decryptdata[AES_BLOCK_SIZE];
/* data structure that contains the key itself */
AES_KEY keyEn;
/* set the encryption key */
AES_set_encrypt_key(ckey, 128, &keyEn);
/* set where on the 128 bit encrypted block to begin encryption*/
int num = 0;
strcpy( (char*)indata , "Hello World" );
bytes_read = sizeof(indata);
AES_cfb128_encrypt(indata, outdata, bytes_read, &keyEn, ivec, &num, AES_ENCRYPT);
cout << "original data:\t" << indata << endl;
cout << "encrypted data:\t" << outdata << endl;
AES_cfb128_encrypt(outdata, decryptdata, bytes_read, &keyEn, ivec, &num, AES_DECRYPT);
cout << "input data was:\t" << decryptdata << endl;
return 0;
}
But the output of "decrypted" data are some random characters, but they are the same after every execution of the code. outdata changes with every execution...
I tried to debug and search for a solution, but I couldn't find any solution for my problem.
Now my question, what is going wrong here? Or do I completely misunderstand the provided functions?
The problem is that AES_cfb128_encrypt modifies the ivec (it has to in order to allow for chaining). Your solution is to create a copy of the ivec and initialize it before each call to AES_cfb128_encrypt as follows:
const char ivecstr[AES_BLOCK_SIZE] = "goodbyworldkey\0";
unsigned char ivec[AES_BLOCK_SIZE];
memcpy( ivec , ivecstr, AES_BLOCK_SIZE);
Then repeat the memcpy before your second call to AES_cfb128_encrypt.
Note 1: Your initial vector was a byte too short, so I put an explicit additional \0 at the end of it. You should make sure all of your strings are of the correct length when copying or passing them.
Note 2: Any code which uses encryption should REALLY avoid using strcpy or any other copy of unchecked length. It's a hazard.
I am trying to get networking information (IP Address, Netmask, Route etc.) for all my interfaces in Qt using NetworkManager DBus interface. The problem is when I try to access the property "Addresses" of org.freedesktop.NetworkManager.IP4Config I get the following error
QDBusAbstractInterface: type QDBusRawType<0x616175>* must be registered with QtDBus before it can be used to read property org.freedesktop.NetworkManager.IP4Config.Addresses
Addresses are invalid
Error 2 = "Unregistered type QDBusRawType<0x616175>* cannot be handled"
However I can get the value of this property using dbus-send with following command.
dbus-send --system --print-reply --dest=org.freedesktop.NetworkManager \
/org/freedesktop/NetworkManager/IP4Config/0 \
org.freedesktop.DBus.Properties.Get \
string:"org.freedesktop.NetworkManager.IP4Config" \
string:"Addresses"
I can also get good values for above interface's mentioned property via qtdbusviewer. Following is my code snippet.
QDBusInterface interface(NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_IFACE, QDBusConnection::systemBus());
// Get a list of all devices
QDBusReply<QList<QDBusObjectPath> > result = interface.call("GetDevices");
foreach (const QDBusObjectPath& connection, result.value()) {
QDBusInterface device(NM_DBUS_SERVICE, connection.path(), "org.freedesktop.NetworkManager.Device", QDBusConnection::systemBus());
if ( device.property("DeviceType").toInt() == NM_DEVICE_TYPE_ETHERNET ) {
// Get the IPv4 information if the device is active
if ( device.property("State").toInt() == NETWORK_DEVICE_CONNECTED ) {
QVariant ipv4config = device.property("Ip4Config");
if ( ipv4config.isValid() ) {
QDBusObjectPath path = qvariant_cast<QDBusObjectPath>(ipv4config);
QDBusInterface ifc(NM_DBUS_SERVICE, path.path(), "org.freedesktop.NetworkManager.IP4Config", QDBusConnection::systemBus());
if ( ifc.isValid() ) {
qDebug() << "Error 1 = " << ifc.lastError().message(); // No error. Everything is OK.
QVariant addresses = ifc.property("Addresses"); // Throwing the QDBusAbstractInterface Error where the property is good and does exist.
if ( addresses.isValid() ) {
qDebug () << "Addresses are valid";
} else {
qDebug () << "Addresses are invalid";
}
qDebug() << "Error 2 = " << ifc.lastError().message();
}
}
}
}
}
UPDATE # 1
I think it appears to be problem of types. Qt-Dbus type system does not understand the type of "Addresses" property so unable to create a QVariant out of it. So I added following lines before reading the property "Addresses". NetworkManager defines the property Addresses as following type, so I guess my typedef is good.
aau - "Array of tuples of IPv4 address/prefix/gateway. All 3 elements of each tuple are in network byte order. Essentially: [(addr, prefix, gateway), (addr, prefix, gateway), ...]"
typedef QList<QList<uint> > Addresses;
Q_DECLARE_METATYPE(Addresses)
qDBusRegisterMetaType<Addresses>()
QVariant addresses = ifc.property("Addresses");
Also I switched to Qt 5.1 (Earlier I was using 4.8), and I am getting the same error in following form.
Cannot construct placeholder type QDBusRawType
Thoughts / Suggestions
Regards,
Farrukh Arshad.
So far as per my research the problem is related to the type conversion. The property value is in the form of aau (as per NM Dbus documentation). QDbusInterface.property returns QVariant. It does find the property but unable to determine the type of the property hence giving me the error message. But my concern is, I have registered the custom type of this property with the Qt Meta Object system as I have mentioned in the Update # 1 then why it is giving me this error my type was registered with the system properly and qDBusRegisterMetaType did returned me a valid integer. In Qt 5.1 the origin of the error is in qdbusmaster.cpp. One article suggests to register meta type as mentioned below, but to no avail.
qRegisterMetaType<Addresses>("Addresses");
qDBusRegisterMetaType<Addresses>();
For now I don't have time to further dig into to see if it is some bug or I am missing something, but I will update this post once I have actual solution.
WORKAROUND
Following workaround will work to read the given property value. For this workaround to work you need to add qdbus-private instead of qdbus and include .
QVariant ipv4config = device.property("Ip4Config");
if ( ipv4config.isValid() ) {
QDBusObjectPath path = qvariant_cast<QDBusObjectPath>(ipv4config);
QDBusMessage message = QDBusMessage::createMethodCall(NM_DBUS_SERVICE, path.path(), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get"));
QList<QVariant> arguments;
arguments << "org.freedesktop.NetworkManager.IP4Config" << "Addresses";
message.setArguments(arguments);
QDBusConnection connection = QDBusConnection::systemBus();
QDBusMessage reply = connection.call(message);
foreach(QVariant var, reply.arguments()) {
qDebug () << "String = " << QDBusUtil::argumentToString(var).toHtmlEscaped();
}
}
The string will show you the IP Address / Subnet Mask / Router IP which you will have to extract from the output. For record, I have taken this approach from qdbusviewer.
This is not the the right solution, but it will get you out of trouble for the time being. There is also a good article suggesting usage of custom types with Qt Dbus.
http://techbase.kde.org/Development/Tutorials/D-Bus/CustomTypes
The best solution I've found for this has been to write a QDBusAbstractInterface implementation:
typedef QList<QList<uint> > UIntListList;
Q_DECLARE_METATYPE(UIntListList)
class DBusIP4ConfigInterface : public QDBusAbstractInterface
{
Q_OBJECT
public:
DBusIP4ConfigInterface(const QString &service, const QString &path, const QDBusConnection &connection,
QObject *parent = 0)
{
qDBusRegisterMetaType<UIntListList>();
}
virtual ~DBusIP4ConfigInterface() { }
Q_PROPERTY(UIntListList Addresses READ addresses)
UIntListList addresses() const
{
return qvariant_cast<UIntListList>(property("Addresses"));
}
Q_PROPERTY(QString Gateway READ gateway)
QString gateway() const
{
return qvariant_cast<QString>(property("Gateway"));
}
Q_SIGNALS:
void PropertiesChanged(const QVariantMap &properties);
};
This has the added advantage of being pretty easy to use:
UIntListList addresses = m_dbusIP4Config->addresses();
Q_ASSERT(addresses.size() >= 1);
Q_ASSERT(addresses[0].size() == 3);
QHostAddress ip = QHostAddress(qFromBigEndian(addresses[0][0]));
I am wondering why GCC is giving me this warning:
test.h: In function TestRegister:
test.h:12577: warning: cast to pointer from integer of different size
Code:
#define Address 0x1234
int TestRegister(unsigned int BaseAddress)
{
unsigned int RegisterValue = 0;
RegisterValue = *((unsigned int *)(BaseAddress + Address)) ;
if((RegisterValue & 0xffffffff) != (0x0 << 0))
{
return(0);
}
else
{
return(1);
}
}
Probably because you're on a 64-bit platform, where pointers are 64-bit but ints are 32-bit.
Rule-of-thumb: Don't try to use integers to store addresses.
If you include <stdint.h> and if you compile for the C99 standard using gcc -Wall -std=c99 you could cast to and from intptr_t which is an integer type of the same size as pointers.
RegisterValue = *((unsigned int *)((intptr_t)(BaseAddress + Address))) ;
Among other things, you're assuming that a pointer will fit into an unsigned int, where C gives no such guarantee… there are a number of platforms in use today where this is untrue, apparently including yours.
A pointer to data can be stored in a (void*) or (type*) safely. Pointers can be added to (or subtracted to yield) a size_t or ssize_t. There's no guaranteed relationship between sizeof(int), sizeof(size_t), sizeof(ssize_t), and (void*) or (type*)…
(Also, in this case, there's no real point in initializing the var and overwriting it on the next line…)
Also unrelated, but you realise that != (0x0 << 0) → != 0 and can be omitted, since if (x) = if (x != 0) … ? Perhaps that's because this is cut down from a larger sample, but that entire routine could be presented as
int TestRegister (unsigned int* BaseAddress)
{ return ( (0xffffffff & *(BaseAddress + Address)) ? 0 : 1 ); }
(Edited: changed to unsigned int* as it seems far more likely he wants to skip through at int-sized offsets?)
I am trying to use a special pointer with a guaranteed invalid address with gcc. Here is what I do:
#define MY_VALUE_OK ((void*)1);
...
int* data;
...
void* d = MY_VALUE_OK;
if( data != ((void*)1) ) // compiles ok
if( data != d ) // compiles ok
if( data != MY_VALUE_OK ) // error!
printf( " %d", *data );
Any ideas?
Ideally I'd like to define this pointer as static const in a class.
BTW, this is my old code that used to compile with Microsoft Visual Studio just fine.
Does your #define include a semi-colon (like your example does)? If so that would allow the assignment to work, but the if statement would error out because after the text substitution, there would be a semi-colon inside the conditional.
You have defined a semicolon with your #define
so...
if(data != MY_VALUE_OK)
Actually turns into:
if(data != ((void*)1);)
There is an obvious error there
Your define should be:
#define MY_VALUE_OK ((void*)1)
That should fix your problem :)