Skip to content

Commit c057e57

Browse files
committedDec 16, 2024·
softhsm2-util: support import certificate
The softhsm2-util already support importing keys, why not also import certificates? Useful for test scripts that require both keys and certificates. Add --import-type <type> parameter, depreciate the --aes parameter. Signed-off-by: Alon Bar-Lev <alon.barlev@gmail.com>
1 parent 4be49e3 commit c057e57

5 files changed

+375
-29
lines changed
 

‎src/bin/util/softhsm2-util-botan.cpp

+61
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
#include <botan/bigint.h>
4848
#include <botan/der_enc.h>
4949
#include <botan/oids.h>
50+
#include <botan/der_enc.h>
51+
#include <botan/x509cert.h>
52+
#include <botan/x509_dn.h>
5053

5154
// Init Botan
5255
void crypto_init()
@@ -209,6 +212,64 @@ int crypto_import_key_pair
209212
return result;
210213
}
211214

215+
int crypto_import_certificate
216+
(
217+
CK_SESSION_HANDLE hSession,
218+
char* filePath,
219+
char* label,
220+
char* objID,
221+
size_t objIDLen
222+
)
223+
{
224+
auto _vector_ptr = [](std::vector<uint8_t> &v) {
225+
return v.size() == 0 ? NULL : &v.front();
226+
};
227+
228+
Botan::X509_Certificate cert(filePath);
229+
std::vector<uint8_t> blob;
230+
std::vector<uint8_t> subject;
231+
std::vector<uint8_t> issuer;
232+
std::vector<uint8_t> serial;
233+
234+
Botan::DER_Encoder blob_encoder(blob);
235+
Botan::DER_Encoder subject_encoder(subject);
236+
Botan::DER_Encoder issuer_encoder(issuer);
237+
Botan::DER_Encoder serial_encoder(serial);
238+
239+
cert.encode_into(blob_encoder);
240+
cert.subject_dn().encode_into(subject_encoder);
241+
cert.issuer_dn().encode_into(issuer_encoder);
242+
serial_encoder.encode(Botan::BigInt::decode(cert.serial_number()));
243+
244+
CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
245+
CK_CERTIFICATE_TYPE certType = CKC_X_509;
246+
CK_BBOOL ckFalse = CK_FALSE, ckTrue = CK_TRUE;
247+
CK_ATTRIBUTE certTemplate[] = {
248+
{ CKA_CLASS, &certClass, sizeof(certClass) },
249+
{ CKA_CERTIFICATE_TYPE, &certType, sizeof(certType) },
250+
{ CKA_LABEL, label, strlen(label) },
251+
{ CKA_ID, objID, objIDLen },
252+
{ CKA_TOKEN, &ckTrue, sizeof(ckTrue) },
253+
{ CKA_PRIVATE, &ckFalse, sizeof(ckFalse) },
254+
{ CKA_VALUE, _vector_ptr(blob), (CK_ULONG)blob.size() },
255+
{ CKA_SUBJECT, _vector_ptr(subject), (CK_ULONG)subject.size() },
256+
{ CKA_ISSUER, _vector_ptr(issuer), (CK_ULONG)issuer.size() },
257+
{ CKA_SERIAL_NUMBER, _vector_ptr(serial), (CK_ULONG)serial.size() }
258+
};
259+
260+
CK_OBJECT_HANDLE hCert;
261+
CK_RV rv = p11->C_CreateObject(hSession, certTemplate, 10, &hCert);
262+
if (rv != CKR_OK)
263+
{
264+
fprintf(stderr, "ERROR: Could not save the certificate in the token.\n");
265+
return 1;
266+
}
267+
268+
printf("The certificate has been imported.\n");
269+
270+
return 0;
271+
}
272+
212273
// Read the key from file
213274
Botan::Private_Key* crypto_read_file(char* filePath, char* filePIN)
214275
{

‎src/bin/util/softhsm2-util-ossl.cpp

+145
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,151 @@ int crypto_import_key_pair
218218
return result;
219219
}
220220

221+
int crypto_import_certificate
222+
(
223+
CK_SESSION_HANDLE hSession,
224+
char* filePath,
225+
char* label,
226+
char* objID,
227+
size_t objIDLen
228+
)
229+
{
230+
BIO* in = NULL;
231+
X509* x509 = NULL;
232+
CK_BYTE_PTR blob = NULL;
233+
CK_BYTE_PTR subject = NULL;
234+
CK_BYTE_PTR issuer = NULL;
235+
CK_BYTE_PTR serial = NULL;
236+
CK_BYTE_PTR p;
237+
int blobSize;
238+
int subjectSize;
239+
int issuerSize;
240+
int serialSize;
241+
int ret = 1;
242+
243+
if (!(in = BIO_new_file(filePath, "rb")))
244+
{
245+
fprintf(stderr, "ERROR: Could open the PKCS#8 file: %s\n", filePath);
246+
goto cleanup;
247+
}
248+
249+
if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL)
250+
{
251+
fprintf(stderr, "ERROR: Could not read the certificate file: %s\n", filePath);
252+
goto cleanup;
253+
}
254+
255+
blobSize = i2d_X509(x509, NULL);
256+
subjectSize = i2d_X509_NAME(X509_get_subject_name(x509), NULL);
257+
issuerSize = i2d_X509_NAME(X509_get_issuer_name(x509), NULL);
258+
serialSize = i2d_ASN1_INTEGER(X509_get_serialNumber(x509), NULL);
259+
260+
if
261+
(
262+
blobSize < 0 ||
263+
subjectSize < 0 ||
264+
issuerSize < 0 ||
265+
serialSize < 0
266+
)
267+
{
268+
fprintf(stderr, "ERROR: Could not convert certificate to DER.\n");
269+
goto cleanup;
270+
}
271+
272+
if (blobSize > 0)
273+
{
274+
if ((blob = (CK_BYTE_PTR)malloc(blobSize)) == NULL)
275+
{
276+
fprintf(stderr, "ERROR: Could not allocate memory.\n");
277+
goto cleanup;
278+
}
279+
p = blob;
280+
blobSize = i2d_X509(x509, &p);
281+
}
282+
if (subjectSize > 0) {
283+
if ((subject = (CK_BYTE_PTR)malloc(subjectSize)) == NULL)
284+
{
285+
fprintf(stderr, "ERROR: Could not allocate memory.\n");
286+
goto cleanup;
287+
}
288+
p = subject;
289+
subjectSize = i2d_X509_NAME(X509_get_subject_name(x509), &p);
290+
}
291+
if (issuerSize > 0)
292+
{
293+
if ((issuer = (CK_BYTE_PTR)malloc(issuerSize)) == NULL)
294+
{
295+
fprintf(stderr, "ERROR: Could not allocate memory.\n");
296+
goto cleanup;
297+
}
298+
p = issuer;
299+
issuerSize = i2d_X509_NAME(X509_get_issuer_name(x509), &p);
300+
}
301+
if (serialSize > 0)
302+
{
303+
if ((serial = (CK_BYTE_PTR)malloc(serialSize)) == NULL)
304+
{
305+
fprintf(stderr, "ERROR: Could not allocate memory.\n");
306+
goto cleanup;
307+
}
308+
p = serial;
309+
serialSize = i2d_ASN1_INTEGER(X509_get_serialNumber(x509), &p);
310+
}
311+
312+
if
313+
(
314+
blobSize < 0 ||
315+
subjectSize < 0 ||
316+
issuerSize < 0 ||
317+
serialSize < 0
318+
)
319+
{
320+
fprintf(stderr, "ERROR: Could not convert certificate to DER.\n");
321+
goto cleanup;
322+
}
323+
324+
{
325+
CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
326+
CK_CERTIFICATE_TYPE certType = CKC_X_509;
327+
CK_BBOOL ckFalse = CK_FALSE, ckTrue = CK_TRUE;
328+
CK_ATTRIBUTE certTemplate[] = {
329+
{ CKA_CLASS, &certClass, sizeof(certClass) },
330+
{ CKA_CERTIFICATE_TYPE, &certType, sizeof(certType) },
331+
{ CKA_LABEL, label, strlen(label) },
332+
{ CKA_ID, objID, objIDLen },
333+
{ CKA_TOKEN, &ckTrue, sizeof(ckTrue) },
334+
{ CKA_PRIVATE, &ckFalse, sizeof(ckFalse) },
335+
{ CKA_VALUE, blob, (CK_ULONG)blobSize },
336+
{ CKA_SUBJECT, subject, (CK_ULONG)subjectSize },
337+
{ CKA_ISSUER, issuer, (CK_ULONG)issuerSize },
338+
{ CKA_SERIAL_NUMBER, serial, (CK_ULONG)serialSize }
339+
};
340+
341+
CK_OBJECT_HANDLE hCert;
342+
CK_RV rv = p11->C_CreateObject(hSession, certTemplate, 10, &hCert);
343+
if (rv != CKR_OK)
344+
{
345+
fprintf(stderr, "ERROR: Could not save the certificate in the token.\n");
346+
goto cleanup;
347+
}
348+
}
349+
350+
printf("The certificate has been imported.\n");
351+
352+
ret = 0;
353+
354+
cleanup:
355+
356+
free(blob);
357+
free(subject);
358+
free(issuer);
359+
free(serial);
360+
X509_free(x509);
361+
BIO_free(in);
362+
363+
return ret;
364+
}
365+
221366
// Read the key from file
222367
EVP_PKEY* crypto_read_file(char* filePath, char* filePIN)
223368
{

‎src/bin/util/softhsm2-util.1

+21-17
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,23 @@ softhsm2-util \- support tool for libsofthsm2
2020
.PP
2121
.B softhsm2-util \-\-import
2222
.I path
23+
.RB [ \-\-import-type
24+
.IR type ]
25+
\\
26+
.ti +0.7i
2327
.RB [ \-\-file-pin
2428
.IR PIN ]
2529
.B \-\-token
2630
.I label
31+
.RB [ \-\-pin
32+
.I PIN]
33+
.B [\-\-no\-public\-key]
2734
\\
2835
.ti +0.7i
29-
.RB [ \-\-pin
30-
.I PIN
31-
.B \-\-no\-public\-key]
3236
.B \-\-label
3337
.I text
3438
.B \-\-id
3539
.I hex
36-
.PP
37-
.B softhsm2-util \-\-import
38-
.I path
39-
.B \-\-aes
40-
.B \-\-token
41-
.I label
42-
\\
43-
.ti +0.7i
4440
.RB [ \-\-pin
4541
.I PIN]
4642
.B \-\-label
@@ -125,11 +121,11 @@ Any content in token will be erased.
125121
Show the help information.
126122
.TP
127123
.B \-\-import \fIpath\fR
128-
Import a key pair from the given
124+
Import an object from the given
129125
.IR path .
130-
The file must be in PKCS#8-format.
131126
.br
132127
Use with
128+
.BR \-\-import-type,
133129
.BR \-\-slot
134130
or
135131
.BR \-\-token
@@ -141,10 +137,6 @@ or
141137
.BR \-\-label ,
142138
and
143139
.BR \-\-id .
144-
.br
145-
Can also be used with
146-
.BR \-\-aes
147-
to use file as is and import it as AES.
148140
.TP
149141
.B \-\-init-token
150142
Initialize the token at a given slot, token label or token serial.
@@ -183,8 +175,20 @@ print the default PKCS#11 library.
183175
Show the version info.
184176
.SH OPTIONS
185177
.TP
178+
.B \-\-import-type \fItype\fR
179+
Import object type, \fItype\fR may be one of:
180+
.RS
181+
.IP keypair\ [default]
182+
The file must be in PKCS#8 PEM format.
183+
.IP aes
184+
The file must be in binary format.
185+
.IP cert
186+
The file must be in X509 PEM format.
187+
.RE
188+
.TP
186189
.B \-\-aes
187190
Used to tell import to use file as is and import it as AES.
191+
Deprecated, use \fI--import-type aes\fR instead.
188192
.TP
189193
.B \-\-file-pin \fIPIN\fR
190194
The

‎src/bin/util/softhsm2-util.cpp

+145-11
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,17 @@ void usage()
101101
printf(" WARNING: Any content in token will be erased.\n");
102102
printf(" -h Shows this help screen.\n");
103103
printf(" --help Shows this help screen.\n");
104-
printf(" --import <path> Import a key pair from the given path.\n");
105-
printf(" The file must be in PKCS#8-format.\n");
106-
printf(" Use with --slot or --token or --serial, --file-pin,\n");
107-
printf(" --label, --id, --no-public-key, and --pin.\n");
104+
printf(" --import <path> Import an object from the given path.\n");
105+
printf(" Use with --import-type, --slot or --token or --serial,\n");
106+
printf(" --file-pin, --label, --id, --no-public-key, and --pin.\n");
107+
printf(" --import-type <type>\n");
108+
printf(" Import object type, type may be one of:\n");
109+
printf(" keypair [default]\n");
110+
printf(" The file must be in PKCS#8 PEM format.\n");
111+
printf(" aes\n");
112+
printf(" The file must be in binary format.\n");
113+
printf(" cert\n");
114+
printf(" The file must be in X509 PEM format.\n");
108115
printf(" --init-token Initialize the token at a given slot.\n");
109116
printf(" Use with --slot or --token or --serial or --free,\n");
110117
printf(" --label, --so-pin, and --pin.\n");
@@ -118,6 +125,7 @@ void usage()
118125
printf(" --version Show version info.\n");
119126
printf("Options:\n");
120127
printf(" --aes Used to tell import to use file as is and import it as AES.\n");
128+
printf(" Deprecated, use '--import-type aes'.\n");
121129
printf(" --file-pin <PIN> Supply a PIN if the file is encrypted.\n");
122130
printf(" --force Used to override a warning.\n");
123131
printf(" --free Use the first free/uninitialized token.\n");
@@ -134,6 +142,13 @@ void usage()
134142
printf(" --token <label> Will use the token with a matching token label.\n");
135143
}
136144

145+
// Enumeration of import types
146+
enum {
147+
IMPORT_TYPE_KEYPAIR,
148+
IMPORT_TYPE_AES,
149+
IMPORT_TYPE_CERT
150+
};
151+
137152
// Enumeration of the long options
138153
enum {
139154
OPT_DELETE_TOKEN = 0x100,
@@ -143,6 +158,7 @@ enum {
143158
OPT_HELP,
144159
OPT_ID,
145160
OPT_IMPORT,
161+
OPT_IMPORT_TYPE,
146162
OPT_INIT_TOKEN,
147163
OPT_LABEL,
148164
OPT_MODULE,
@@ -167,6 +183,7 @@ static const struct option long_options[] = {
167183
{ "help", 0, NULL, OPT_HELP },
168184
{ "id", 1, NULL, OPT_ID },
169185
{ "import", 1, NULL, OPT_IMPORT },
186+
{ "import-type", 1, NULL, OPT_IMPORT_TYPE },
170187
{ "init-token", 0, NULL, OPT_INIT_TOKEN },
171188
{ "label", 1, NULL, OPT_LABEL },
172189
{ "module", 1, NULL, OPT_MODULE },
@@ -205,7 +222,7 @@ int main(int argc, char* argv[])
205222
int forceExec = 0;
206223
bool freeToken = false;
207224
int noPublicKey = 0;
208-
bool importAES = false;
225+
int importType = IMPORT_TYPE_KEYPAIR;
209226

210227
int doInitToken = 0;
211228
int doShowSlots = 0;
@@ -251,8 +268,20 @@ int main(int argc, char* argv[])
251268
inPath = optarg;
252269
needP11 = true;
253270
break;
271+
case OPT_IMPORT_TYPE:
272+
if (!strcmp(optarg, "keypair"))
273+
importType = IMPORT_TYPE_KEYPAIR;
274+
else if (!strcmp(optarg, "aes"))
275+
importType = IMPORT_TYPE_AES;
276+
else if (!strcmp(optarg, "cert"))
277+
importType = IMPORT_TYPE_CERT;
278+
else
279+
{
280+
fprintf(stderr, "ERROR: Invalid import type '%s'\n", optarg);
281+
}
282+
break;
254283
case OPT_AES:
255-
importAES = true;
284+
importType = IMPORT_TYPE_AES;
256285
break;
257286
case OPT_DELETE_TOKEN:
258287
doDeleteToken = 1;
@@ -370,8 +399,22 @@ int main(int argc, char* argv[])
370399
rv = findSlot(slot, serial, token, slotID);
371400
if (!rv)
372401
{
373-
rv = importAES ? importSecretKey(inPath, slotID, userPIN, label, objectID)
374-
: importKeyPair(inPath, filePIN, slotID, userPIN, label, objectID, forceExec, noPublicKey);
402+
switch(importType)
403+
{
404+
case IMPORT_TYPE_KEYPAIR:
405+
rv = importKeyPair(inPath, filePIN, slotID, userPIN, label, objectID, forceExec, noPublicKey);
406+
break;
407+
case IMPORT_TYPE_AES:
408+
rv = importSecretKey(inPath, slotID, userPIN, label, objectID);
409+
break;
410+
case IMPORT_TYPE_CERT:
411+
rv = importCertificate(inPath, slotID, userPIN, label, objectID, forceExec);
412+
break;
413+
default:
414+
fprintf(stderr, "Invalid importType %d.\n", importType);
415+
rv = 1;
416+
break;
417+
}
375418
}
376419
}
377420

@@ -1117,7 +1160,7 @@ int importKeyPair
11171160
return 1;
11181161
}
11191162

1120-
CK_OBJECT_HANDLE oHandle = searchObject(hSession, objID, objIDLen);
1163+
CK_OBJECT_HANDLE oHandle = searchObject(hSession, CKO_PRIVATE_KEY, objID, objIDLen);
11211164
if (oHandle != CK_INVALID_HANDLE && forceExec == 0)
11221165
{
11231166
free(objID);
@@ -1205,6 +1248,98 @@ int importSecretKey(char* filePath, CK_SLOT_ID slotID, char* userPIN, char* labe
12051248
return result;
12061249
}
12071250

1251+
// Import a certificate from given path
1252+
int importCertificate
1253+
(
1254+
char* filePath,
1255+
CK_SLOT_ID slotID,
1256+
char* userPIN,
1257+
char* label,
1258+
char* objectID,
1259+
int forceExec
1260+
)
1261+
{
1262+
char user_pin_copy[MAX_PIN_LEN+1] = {0};
1263+
1264+
if (label == NULL)
1265+
{
1266+
fprintf(stderr, "ERROR: A label for the object must be supplied. "
1267+
"Use --label <text>\n");
1268+
return 1;
1269+
}
1270+
1271+
if (objectID == NULL)
1272+
{
1273+
fprintf(stderr, "ERROR: An ID for the object must be supplied. "
1274+
"Use --id <hex>\n");
1275+
return 1;
1276+
}
1277+
1278+
size_t objIDLen = 0;
1279+
char* objID = hexStrToBin(objectID, strlen(objectID), &objIDLen);
1280+
if (objID == NULL)
1281+
{
1282+
fprintf(stderr, "Please edit --id <hex> to correct error.\n");
1283+
return 1;
1284+
}
1285+
1286+
CK_SESSION_HANDLE hSession;
1287+
CK_RV rv = p11->C_OpenSession(slotID, CKF_SERIAL_SESSION | CKF_RW_SESSION,
1288+
NULL_PTR, NULL_PTR, &hSession);
1289+
if (rv != CKR_OK)
1290+
{
1291+
if (rv == CKR_SLOT_ID_INVALID)
1292+
{
1293+
fprintf(stderr, "ERROR: The given slot does not exist.\n");
1294+
}
1295+
else
1296+
{
1297+
fprintf(stderr, "ERROR: Could not open a session on the given slot.\n");
1298+
}
1299+
free(objID);
1300+
return 1;
1301+
}
1302+
1303+
// Get the password
1304+
if (getPW(userPIN, user_pin_copy, CKU_USER) != 0)
1305+
{
1306+
fprintf(stderr, "ERROR: Could not get user PIN\n");
1307+
free(objID);
1308+
return 1;
1309+
}
1310+
1311+
rv = p11->C_Login(hSession, CKU_USER, (CK_UTF8CHAR_PTR)user_pin_copy, strlen(user_pin_copy));
1312+
if (rv != CKR_OK)
1313+
{
1314+
if (rv == CKR_PIN_INCORRECT) {
1315+
fprintf(stderr, "ERROR: The given user PIN does not match the one in the token.\n");
1316+
}
1317+
else
1318+
{
1319+
fprintf(stderr, "ERROR: Could not log in on the token.\n");
1320+
}
1321+
free(objID);
1322+
return 1;
1323+
}
1324+
1325+
CK_OBJECT_HANDLE oHandle = searchObject(hSession, CKO_CERTIFICATE, objID, objIDLen);
1326+
if (oHandle != CK_INVALID_HANDLE && forceExec == 0)
1327+
{
1328+
free(objID);
1329+
fprintf(stderr, "ERROR: The ID is already assigned to another object. "
1330+
"Use --force to override this message.\n");
1331+
return 1;
1332+
}
1333+
1334+
crypto_init();
1335+
int result = crypto_import_certificate(hSession, filePath, label, objID, objIDLen);
1336+
crypto_final();
1337+
1338+
free(objID);
1339+
1340+
return result;
1341+
}
1342+
12081343
// Convert a char array of hexadecimal characters into a binary representation
12091344
char* hexStrToBin(char* objectID, int idLength, size_t* newLen)
12101345
{
@@ -1291,14 +1426,13 @@ int hexdigit_to_int(char ch)
12911426
}
12921427

12931428
// Search for an object
1294-
CK_OBJECT_HANDLE searchObject(CK_SESSION_HANDLE hSession, char* objID, size_t objIDLen)
1429+
CK_OBJECT_HANDLE searchObject(CK_SESSION_HANDLE hSession, CK_OBJECT_CLASS oClass, char* objID, size_t objIDLen)
12951430
{
12961431
if (objID == NULL)
12971432
{
12981433
return CK_INVALID_HANDLE;
12991434
}
13001435

1301-
CK_OBJECT_CLASS oClass = CKO_PRIVATE_KEY;
13021436
CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;
13031437
CK_ULONG objectCount = 0;
13041438

‎src/bin/util/softhsm2-util.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ bool rm(std::string path);
4949
int showSlots();
5050
int importKeyPair(char* filePath, char* filePIN, CK_SLOT_ID slotID, char* userPIN, char* objectLabel, char* objectID, int forceExec, int noPublicKey);
5151
int importSecretKey(char* filePath, CK_SLOT_ID slotID, char* userPIN, char* label, char* objectID);
52+
int importCertificate(char* filePath, CK_SLOT_ID slotID, char* userPIN, char* objectLabel, char* objectID, int forceExec);
5253
int crypto_import_key_pair(CK_SESSION_HANDLE hSession, char* filePath, char* filePIN, char* label, char* objID, size_t objIDLen, int noPublicKey);
5354
int crypto_import_aes_key(CK_SESSION_HANDLE hSession, char* filePath, char* label, char* objID, size_t objIDLen);
55+
int crypto_import_certificate(CK_SESSION_HANDLE hSession, char* filePath, char* label, char* objID, size_t objIDLen);
5456

5557
// Support functions
5658

@@ -72,6 +74,6 @@ static void* moduleHandle;
7274
extern CK_FUNCTION_LIST_PTR p11;
7375

7476
/// PKCS#11 support
75-
CK_OBJECT_HANDLE searchObject(CK_SESSION_HANDLE hSession, char* objID, size_t objIDLen);
77+
CK_OBJECT_HANDLE searchObject(CK_SESSION_HANDLE hSession, CK_OBJECT_CLASS oClass, char* objID, size_t objIDLen);
7678

7779
#endif // !_SOFTHSM_V2_SOFTHSM2_UTIL_H

0 commit comments

Comments
 (0)
Please sign in to comment.