1 /** 2 * --------- 3 * |.##> <##.| SmartCard-HSM Support Scripts 4 * |# #| 5 * |# #| Copyright (c) 2011-2012 CardContact Software & System Consulting 6 * |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de) 7 * --------- 8 * 9 * This file is part of OpenSCDP. 10 * 11 * OpenSCDP is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 * 15 * OpenSCDP is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with OpenSCDP; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 * @fileoverview Class implementing a key store for X.509 certificate and private keys stored on a SmartCard-HSM 25 */ 26 27 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference; 28 var SmartCardHSM = require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM; 29 var SmartCardHSMKey = require('scsh/sc-hsm/SmartCardHSM').SmartCardHSMKey; 30 var CVC = require("scsh/eac/CVC").CVC; 31 32 33 34 /** 35 * Create a simple key store front-end 36 * 37 * @class Class implementing some simple access functions to generate key pairs and store certificates 38 * @param {SmartCardHSM} sc the SmartCard-HSM card service 39 */ 40 function HSMKeyStore(sc) { 41 this.sc = sc; 42 this.chr = new PublicKeyReference("UTNULL00000"); 43 this.car = new PublicKeyReference("UTNULL00000"); 44 } 45 46 exports.HSMKeyStore = HSMKeyStore; 47 48 49 50 /** 51 * Generate a key pair 52 * 53 * @param {String} label the label under which the key pair shall be stored 54 * @param {SmartCardHSMKeySpecGenerator} initialized key spec generator 55 * @type CVC 56 * @return the authenticated request 57 */ 58 HSMKeyStore.prototype.generateKeyPair = function(label, spec) { 59 60 if ((spec.keytype != Crypto.RSA) && (spec.keytype != Crypto.EC)) { 61 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Unsupported key type"); 62 } 63 64 var key = this.sc.getKey(label); 65 if (key) { 66 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Key with label " + label + " does already exist"); 67 } 68 69 var newkid = this.sc.determineFreeKeyId(); 70 var reqbin = this.sc.generateAsymmetricKeyPair(newkid, 0, spec.encode()); 71 var req = new CVC(reqbin); 72 var keyid = req.determineKeyIdentifier(); 73 74 switch(spec.keytype) { 75 case Crypto.RSA: 76 var keydesc = SmartCardHSM.buildPrkDforRSA(keyid, label, spec.keysize); 77 break; 78 case Crypto.EC: 79 var keydesc = SmartCardHSM.buildPrkDforECC(keyid, label, spec.domainParameter.getSize()); 80 break; 81 default: 82 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Unsupported key type"); 83 } 84 85 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 86 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 87 88 var hkey = new SmartCardHSMKey(this.sc, newkid); 89 hkey.setDescription(keydesc); 90 this.sc.addKeyToMap(hkey); 91 return req; 92 } 93 94 95 96 /** 97 * Generate a symmetric key 98 * 99 * @param {String} label the label under which the key shall be stored 100 * @param {SmartCardHSMKeySpecGenerator} initialized key spec generator 101 * @type ByteString 102 * @return the new key wrapped with the symmetric key defined with SmartCardHSMKeySpecGenerator.setWrappingKey() 103 */ 104 HSMKeyStore.prototype.generateKey = function(label, spec) { 105 106 if (spec.keytype != Crypto.AES) { 107 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Unsupported key type"); 108 } 109 110 var key = this.sc.getKey(label); 111 if (key) { 112 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Key with label " + label + " does already exist"); 113 } 114 115 var newkid = this.sc.determineFreeKeyId(); 116 117 var genalg; 118 switch(spec.keysize) { 119 case 128: genalg = 0xB0; break; 120 case 192: genalg = 0xB1; break; 121 case 256: genalg = 0xB2; break; 122 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Unsupported key size"); 123 } 124 125 var wrapbin = this.sc.generateSymmetricKey(newkid, genalg, spec.encode()); 126 127 var deralg; 128 if (spec.algorithms.find(ByteString.valueOf(SmartCardHSM.ALG_CMAC)) >= 0) { 129 deralg = SmartCardHSM.ALG_CMAC; 130 } else if (spec.algorithms.find(ByteString.valueOf(SmartCardHSM.ALG_DERIVE_SP800_56C)) >= 0) { 131 deralg = SmartCardHSM.ALG_DERIVE_SP800_56C; 132 } 133 134 var keyid; 135 if (deralg) { 136 keyid = this.sc.deriveSymmetricKey(newkid, deralg, new ByteString("KeyCheckValue", ASCII)).left(8); 137 } else { 138 keyid = ByteString.valueOf(newkid); 139 } 140 141 var keydesc = SmartCardHSM.buildSKDforAES(keyid, label, spec.keysize); 142 143 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 144 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 145 146 var hkey = new SmartCardHSMKey(this.sc, newkid); 147 hkey.setDescription(keydesc); 148 this.sc.addKeyToMap(hkey); 149 return wrapbin; 150 } 151 152 153 154 /** 155 * Generate a RSA key pair 156 * 157 * @param {String} label the label under which the key pair shall be stored 158 * @param {Number} keysize the key size in bits (1024, 1536 or 2048) 159 */ 160 HSMKeyStore.prototype.generateRSAKeyPair = function(label, keysize) { 161 162 var key = this.sc.getKey(label); 163 if (key) { 164 var newkid = key.getId(); 165 } else { 166 var newkid = this.sc.determineFreeKeyId(); 167 } 168 169 var algo = new ByteString("id-TA-RSA-v1-5-SHA-256", OID); 170 171 var keydata = SmartCardHSM.buildGAKPwithRSA(this.car, algo, this.chr, keysize); 172 var keydesc = SmartCardHSM.buildPrkDforRSA(newkid, label, keysize); 173 174 var reqbin = this.sc.generateAsymmetricKeyPair(newkid, 0, keydata); 175 176 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 177 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 178 var req = new CVC(reqbin); 179 180 var hkey = new SmartCardHSMKey(this.sc, newkid); 181 hkey.setDescription(keydesc); 182 this.sc.addKeyToMap(hkey); 183 return req; 184 } 185 186 187 188 /** 189 * Generate an ECDSA key pair 190 * 191 * @param {String} label the label under which the key pair shall be stored 192 * @param {String} curve the curve object identifier 193 */ 194 HSMKeyStore.prototype.generateECCKeyPair = function(label, curve) { 195 196 var key = this.sc.getKey(label); 197 if (key) { 198 var newkid = key.getId(); 199 } else { 200 var newkid = this.sc.determineFreeKeyId(); 201 } 202 203 var algo = new ByteString("id-TA-ECDSA-SHA-256", OID); 204 205 var dp = new Key(); 206 dp.setComponent(Key.ECC_CURVE_OID, new ByteString(curve, OID)); 207 208 var keydata = SmartCardHSM.buildGAKPwithECC(this.car, algo, this.chr, dp); 209 // print("Keysize: " + dp.getSize()); 210 var keydesc = SmartCardHSM.buildPrkDforECC(newkid, label, dp.getSize()); 211 212 var reqbin = this.sc.generateAsymmetricKeyPair(newkid, 0, keydata); 213 214 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 215 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 216 var req = new CVC(reqbin); 217 218 var hkey = new SmartCardHSMKey(this.sc, newkid); 219 hkey.setDescription(keydesc); 220 this.sc.addKeyToMap(hkey); 221 222 return req; 223 } 224 225 226 227 /** 228 * Import a key blob, meta data and certificate 229 * 230 * @param {ByteString} keywrap the binary key in SmartCard-HSM format 231 */ 232 HSMKeyStore.prototype.importKey = function(keywrap) { 233 234 var a = new ASN1(keywrap); 235 236 var wrap = a.get(0).value; 237 var id = this.sc.determineFreeKeyId(); 238 239 if (id < 0) { 240 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Key identifier exhausted"); 241 } 242 243 this.sc.unwrapKey(id, wrap); 244 245 var hkey = new SmartCardHSMKey(this.sc, id); 246 247 if (a.elements > 1) { 248 var meta = a.get(1); 249 this.sc.updateBinary(ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + id), 0, meta.getBytes()); 250 hkey.setDescription(meta); 251 } 252 253 var bin = new ByteString("", HEX); 254 for (var i = 2; i < a.elements; i++) { 255 var cert = a.get(i); 256 bin = bin.concat(cert.getBytes()); 257 } 258 this.sc.updateBinary(ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + id), 0, bin); 259 260 this.sc.addKeyToMap(hkey); 261 262 return hkey; 263 } 264 265 266 267 /** 268 * Export a key blob, meta data and certificate 269 * 270 * @param {String/Number/Key} labelOrIdOrKey the label, id or object of the key to be removed 271 * @type ByteString 272 * @return the blob with key, meta data and certificate 273 */ 274 HSMKeyStore.prototype.exportKey = function(labelOrIdOrKey) { 275 276 var key = labelOrIdOrKey; 277 if (!(key instanceof SmartCardHSMKey)) { 278 key = this.getKey(labelOrIdOrKey); 279 } 280 281 if (key) { 282 var id = key.getId(); 283 } else { 284 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label or id " + labelOrIdOrKey); 285 } 286 287 var wrap = this.sc.wrapKey(id); 288 var a = new ASN1(0x30, new ASN1(0x04, wrap)); 289 290 try { 291 var meta = this.sc.readBinary(ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + id)); 292 a.add(new ASN1(meta)); 293 294 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + id); 295 if (this.sc.hasFile(fid)) { 296 var cert = this.sc.readBinary(fid); 297 298 if (cert.byteAt(0) == 0x67) { 299 var list = SmartCardHSM.parseCertificateList(cert); 300 for (var i = 0; i < list.length; i++) { 301 a.add(list[i].getASN1()); 302 } 303 if (list.length == 1) { 304 print("Adding device and DICA certificate to export"); 305 var devAutCert = this.sc.readBinary(SmartCardHSM.C_DevAut); 306 var list = SmartCardHSM.parseCertificateList(devAutCert); 307 for (var i = 0; i < list.length; i++) { 308 a.add(list[i].getASN1()); 309 } 310 } 311 } else { 312 a.add(new ASN1(cert)); 313 } 314 } 315 } 316 catch(e) { 317 print("Ignoring meta data or certificate: " + e); 318 } 319 320 return a.getBytes(); 321 } 322 323 324 325 /** 326 * Import a RSA key blob 327 * 328 * @param {String} label the key label 329 * @param {ByteString} keyblob the binary key in SmartCard-HSM format 330 * @param {Number} keysize in bits 331 * @param {Number/ByteString} keyid the optional PKCS#15 key identifier. 332 * Default value is the next free key id of the SmartCard-HSM. 333 * @type SmartCardHSMKey 334 */ 335 HSMKeyStore.prototype.importRSAKey = function(label, keyblob, keysize, keyid) { 336 var keytype = keyblob.byteAt(8); 337 if ((keytype != 5) && (keytype != 6)) { 338 throw new GPError(module.id, GPError.INVALID_DATA, 0, "keyblob does not contain a RSA key"); 339 } 340 341 var key = this.sc.getKey(label); 342 if (key) { 343 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "A key with label " + label + " does already exist"); 344 } 345 var newkid = this.sc.determineFreeKeyId(); 346 347 this.sc.unwrapKey(newkid, keyblob); 348 349 if (typeof(keyid) == "undefined") { 350 keyid = newkid; 351 } 352 353 var keydesc = SmartCardHSM.buildPrkDforRSA(keyid, label, keysize); 354 355 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 356 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 357 358 var hkey = new SmartCardHSMKey(this.sc, newkid); 359 hkey.setDescription(keydesc); 360 this.sc.addKeyToMap(hkey); 361 362 return hkey; 363 } 364 365 366 367 /** 368 * Import an ECC key blob 369 * 370 * @param {String} label the key label 371 * @param {ByteString} keyblob the binary key in SmartCard-HSM format 372 * @param {Number} keysize in bits 373 * @param {Number/ByteString} keyid the optional PKCS#15 key identifier. 374 * Default value is the next free key id of the SmartCard-HSM. 375 * @type SmartCardHSMKey 376 */ 377 HSMKeyStore.prototype.importECCKey = function(label, keyblob, keysize, keyid) { 378 var keytype = keyblob.byteAt(8); 379 if (keytype != 12) { 380 throw new GPError(module.id, GPError.INVALID_DATA, 0, "keyblob does not contain an ECC key"); 381 } 382 var key = this.sc.getKey(label); 383 if (key) { 384 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "A key with label " + label + " does already exist"); 385 } 386 var newkid = this.sc.determineFreeKeyId(); 387 388 this.sc.unwrapKey(newkid, keyblob); 389 390 if (typeof(keyid) == "undefined") { 391 keyid = newkid; 392 } 393 394 var keydesc = SmartCardHSM.buildPrkDforECC(keyid, label, keysize); 395 396 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 397 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 398 399 var hkey = new SmartCardHSMKey(this.sc, newkid); 400 hkey.setDescription(keydesc); 401 this.sc.addKeyToMap(hkey); 402 403 return hkey; 404 } 405 406 407 408 /** 409 * Import an AES key blob 410 * 411 * @param {String} label the key label 412 * @param {ByteString} keyblob the binary key in SmartCard-HSM format 413 * @param {Number} keysize in bits 414 * @param {ByteString} keyid the PKCS#15 key id (CKA_ID) 415 * @type 416 */ 417 HSMKeyStore.prototype.importAESKey = function(label, keyblob, keysize, keyid) { 418 419 var key = this.sc.getKey(label); 420 if (key) { 421 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "A key with label " + label + " does already exist"); 422 } 423 var newkid = this.sc.determineFreeKeyId(); 424 425 this.sc.unwrapKey(newkid, keyblob); 426 427 if (typeof(keyid) == "undefined") { 428 keyid = newkid; 429 } 430 431 var keydesc = SmartCardHSM.buildSKDforAES(keyid, label, keysize); 432 433 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 434 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 435 436 var hkey = new SmartCardHSMKey(this.sc, newkid); 437 hkey.setDescription(keydesc); 438 this.sc.addKeyToMap(hkey); 439 440 return hkey; 441 } 442 443 444 445 /** 446 * Store certificate under given label 447 * 448 * @param {String/Number/Key} labelOrIdOrKey the label, id or object of the key for which the certificate should be stored 449 * @param {X509} cert the certificate 450 */ 451 HSMKeyStore.prototype.storeEndEntityCertificate = function(labelOrIdOrKey, cert) { 452 var key = labelOrIdOrKey; 453 if (!(key instanceof SmartCardHSMKey)) { 454 key = this.getKey(labelOrIdOrKey); 455 } 456 457 if (key) { 458 var kid = key.getId(); 459 } else { 460 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label " + label); 461 } 462 463 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 464 if (!(cert instanceof ByteString)) { 465 cert = cert.getBytes(); 466 } 467 this.sc.updateBinary(fid, 0, cert); 468 } 469 470 471 472 /** 473 * Store CA certificate under given label 474 * 475 * @param {String} label the label under which the certificate shall be stored 476 * @param {X509} cert the certificate 477 */ 478 HSMKeyStore.prototype.storeCACertificate = function(label, cert) { 479 this.sc.enumerateCACertificates(); 480 var id = this.sc.caidmap[label]; 481 if (id == undefined) { 482 id = this.sc.determineFreeCAId(); 483 } 484 485 var fid = ByteString.valueOf((SmartCardHSM.CACERTIFICATEPREFIX << 8) + id); 486 this.sc.updateBinary(fid, 0, cert.getBytes()); 487 488 var descr = SmartCardHSM.buildCertDescription(label, null, cert.getPublicKey(), fid); 489 var fid = ByteString.valueOf((SmartCardHSM.CERTDPREFIX << 8) + id); 490 this.sc.updateBinary(fid, 0, descr.getBytes()); 491 this.sc.addCACertificateToMap(cert, id, label); 492 } 493 494 495 496 /** 497 * Delete CA certificate with given label 498 * 499 * @param {String} label the label of certificate to be removed 500 */ 501 HSMKeyStore.prototype.deleteCACertificate = function(label) { 502 var cert = this.sc.getCACertificate(label); 503 504 if (!cert) { 505 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a certificate with label " + label); 506 } 507 508 var fid = ByteString.valueOf((SmartCardHSM.CERTDPREFIX << 8) + cert.id); 509 this.sc.deleteFile(fid); 510 var fid = ByteString.valueOf((SmartCardHSM.CACERTIFICATEPREFIX << 8) + cert.id); 511 this.sc.deleteFile(fid); 512 this.sc.enumerateCACertificates(); 513 } 514 515 516 517 /** 518 * Delete key and certificate with given label 519 * 520 * @param {String/Number/Key} labelOrIdOrKey the label, id or object of the key to be removed 521 */ 522 HSMKeyStore.prototype.deleteKey = function(labelOrIdOrKey) { 523 var key = labelOrIdOrKey; 524 if (!(key instanceof SmartCardHSMKey)) { 525 key = this.getKey(labelOrIdOrKey); 526 } 527 528 if (key) { 529 var kid = key.getId(); 530 } else { 531 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label " + labelOrIdOrKey); 532 } 533 534 var fid = ByteString.valueOf((SmartCardHSM.KEYPREFIX << 8) + kid); 535 this.sc.deleteFile(fid); 536 537 try { 538 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + kid); 539 this.sc.deleteFile(fid); 540 541 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 542 this.sc.deleteFile(fid); 543 } 544 catch(e) { 545 // Ignore 546 } 547 548 this.enumerateKeys(); 549 } 550 551 552 553 /** 554 * Return list of keys 555 * 556 * @type String[] 557 * @return the list of key names 558 */ 559 HSMKeyStore.prototype.enumerateKeys = function() { 560 return this.sc.enumerateKeys(); 561 } 562 563 564 565 /** 566 * Check if key with label exists 567 * 568 * @param {String / Number} the key label or id 569 * @type Boolean 570 * @return true if key exists 571 */ 572 HSMKeyStore.prototype.hasKey = function(labelOrId) { 573 var key = this.sc.getKey(labelOrId); 574 return key != null; 575 } 576 577 578 579 /** 580 * Get key for given label 581 * 582 * @param {String / Number} the key label or id 583 * @type Key 584 * @return the key 585 */ 586 HSMKeyStore.prototype.getKey = function(labelOrId) { 587 var key = this.sc.getKey(labelOrId); 588 if (!key) { 589 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label/id " + labelOrId); 590 } 591 return key; 592 } 593 594 595 596 /** 597 * Check if key has a certificate 598 * 599 * @param {String/Number/Key} labelOrIdOrKey the certificate label, id or key 600 * @type Boolean 601 * @return true of a certificate is present 602 */ 603 HSMKeyStore.prototype.hasCertificate = function(labelOrIdOrKey) { 604 var key = labelOrIdOrKey; 605 if (!(key instanceof SmartCardHSMKey)) { 606 key = this.getKey(labelOrIdOrKey); 607 } 608 var kid = key.getId(); 609 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 610 return this.sc.hasFile(fid); 611 } 612 613 614 615 /** 616 * Get raw certificate for given label 617 * 618 * @param {String/Number/Key} labelOrIdOrKey the certificate label, id or key 619 * @type ByteString 620 * @return the certificate 621 */ 622 HSMKeyStore.prototype.getCertificate = function(labelOrIdOrKey) { 623 var key = labelOrIdOrKey; 624 if (!(key instanceof SmartCardHSMKey)) { 625 key = this.getKey(labelOrIdOrKey); 626 } 627 var kid = key.getId(); 628 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 629 var certbin = this.sc.readBinary(fid); 630 return certbin; 631 } 632 633 634 635 /** 636 * Get certificate for given label 637 * 638 * @param {String/Number/Key} labelOrIdOrKey the certificate label, id or key 639 * @type X509 640 * @return the certificate 641 */ 642 HSMKeyStore.prototype.getEndEntityCertificate = function(labelOrIdOrKey) { 643 var certbin = this.getCertificate(labelOrIdOrKey); 644 return new X509(certbin); 645 } 646