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 // Copy list of CVCs 299 if ((cert.byteAt(0) == 0x67) || (cert.byteAt(0) == 0x7F)) { 300 var list = SmartCardHSM.parseCertificateList(cert); 301 for (var i = 0; i < list.length; i++) { 302 a.add(list[i].getASN1()); 303 } 304 if ((cert.byteAt(0) == 0x67) && (list.length == 1)) { 305 print("Adding device and DICA certificate to export"); 306 var devAutCert = this.sc.readBinary(SmartCardHSM.C_DevAut); 307 var list = SmartCardHSM.parseCertificateList(devAutCert); 308 for (var i = 0; i < list.length; i++) { 309 a.add(list[i].getASN1()); 310 } 311 } 312 } else { 313 a.add(new ASN1(cert)); 314 } 315 } 316 } 317 catch(e) { 318 print("Ignoring meta data or certificate: " + e); 319 } 320 321 return a.getBytes(); 322 } 323 324 325 326 /** 327 * Import a RSA key blob 328 * 329 * @param {String} label the key label 330 * @param {ByteString} keyblob the binary key in SmartCard-HSM format 331 * @param {Number} keysize in bits 332 * @param {Number/ByteString} keyid the optional PKCS#15 key identifier. 333 * Default value is the next free key id of the SmartCard-HSM. 334 * @type SmartCardHSMKey 335 */ 336 HSMKeyStore.prototype.importRSAKey = function(label, keyblob, keysize, keyid) { 337 var keytype = keyblob.byteAt(8); 338 if ((keytype != 5) && (keytype != 6)) { 339 throw new GPError(module.id, GPError.INVALID_DATA, 0, "keyblob does not contain a RSA key"); 340 } 341 342 var key = this.sc.getKey(label); 343 if (key) { 344 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "A key with label " + label + " does already exist"); 345 } 346 var newkid = this.sc.determineFreeKeyId(); 347 348 this.sc.unwrapKey(newkid, keyblob); 349 350 if (typeof(keyid) == "undefined") { 351 keyid = newkid; 352 } 353 354 var keydesc = SmartCardHSM.buildPrkDforRSA(keyid, label, keysize); 355 356 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 357 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 358 359 var hkey = new SmartCardHSMKey(this.sc, newkid); 360 hkey.setDescription(keydesc); 361 this.sc.addKeyToMap(hkey); 362 363 return hkey; 364 } 365 366 367 368 /** 369 * Import an ECC key blob 370 * 371 * @param {String} label the key label 372 * @param {ByteString} keyblob the binary key in SmartCard-HSM format 373 * @param {Number} keysize in bits 374 * @param {Number/ByteString} keyid the optional PKCS#15 key identifier. 375 * Default value is the next free key id of the SmartCard-HSM. 376 * @type SmartCardHSMKey 377 */ 378 HSMKeyStore.prototype.importECCKey = function(label, keyblob, keysize, keyid) { 379 var keytype = keyblob.byteAt(8); 380 if (keytype != 12) { 381 throw new GPError(module.id, GPError.INVALID_DATA, 0, "keyblob does not contain an ECC key"); 382 } 383 var key = this.sc.getKey(label); 384 if (key) { 385 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "A key with label " + label + " does already exist"); 386 } 387 var newkid = this.sc.determineFreeKeyId(); 388 389 this.sc.unwrapKey(newkid, keyblob); 390 391 if (typeof(keyid) == "undefined") { 392 keyid = newkid; 393 } 394 395 var keydesc = SmartCardHSM.buildPrkDforECC(keyid, label, keysize); 396 397 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 398 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 399 400 var hkey = new SmartCardHSMKey(this.sc, newkid); 401 hkey.setDescription(keydesc); 402 this.sc.addKeyToMap(hkey); 403 404 return hkey; 405 } 406 407 408 409 /** 410 * Import an AES key blob 411 * 412 * @param {String} label the key label 413 * @param {ByteString} keyblob the binary key in SmartCard-HSM format 414 * @param {Number} keysize in bits 415 * @param {ByteString} keyid the PKCS#15 key id (CKA_ID) 416 * @type 417 */ 418 HSMKeyStore.prototype.importAESKey = function(label, keyblob, keysize, keyid) { 419 420 var key = this.sc.getKey(label); 421 if (key) { 422 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "A key with label " + label + " does already exist"); 423 } 424 var newkid = this.sc.determineFreeKeyId(); 425 426 this.sc.unwrapKey(newkid, keyblob); 427 428 if (typeof(keyid) == "undefined") { 429 keyid = newkid; 430 } 431 432 var keydesc = SmartCardHSM.buildSKDforAES(keyid, label, keysize); 433 434 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + newkid); 435 this.sc.updateBinary(fid, 0, keydesc.getBytes()); 436 437 var hkey = new SmartCardHSMKey(this.sc, newkid); 438 hkey.setDescription(keydesc); 439 this.sc.addKeyToMap(hkey); 440 441 return hkey; 442 } 443 444 445 446 /** 447 * Store certificate under given label 448 * 449 * @param {String/Number/Key} labelOrIdOrKey the label, id or object of the key for which the certificate should be stored 450 * @param {X509} cert the certificate 451 */ 452 HSMKeyStore.prototype.storeEndEntityCertificate = function(labelOrIdOrKey, cert) { 453 var key = labelOrIdOrKey; 454 if (!(key instanceof SmartCardHSMKey)) { 455 key = this.getKey(labelOrIdOrKey); 456 } 457 458 if (key) { 459 var kid = key.getId(); 460 } else { 461 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label " + label); 462 } 463 464 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 465 if (!(cert instanceof ByteString)) { 466 cert = cert.getBytes(); 467 } 468 this.sc.updateBinary(fid, 0, cert); 469 } 470 471 472 473 /** 474 * Store CA certificate under given label 475 * 476 * @param {String} label the label under which the certificate shall be stored 477 * @param {X509} cert the certificate 478 */ 479 HSMKeyStore.prototype.storeCACertificate = function(label, cert) { 480 this.sc.enumerateCACertificates(); 481 var id = this.sc.caidmap[label]; 482 if (id == undefined) { 483 id = this.sc.determineFreeCAId(); 484 } 485 486 var fid = ByteString.valueOf((SmartCardHSM.CACERTIFICATEPREFIX << 8) + id); 487 this.sc.updateBinary(fid, 0, cert.getBytes()); 488 489 var descr = SmartCardHSM.buildCertDescription(label, null, cert.getPublicKey(), fid); 490 var fid = ByteString.valueOf((SmartCardHSM.CERTDPREFIX << 8) + id); 491 this.sc.updateBinary(fid, 0, descr.getBytes()); 492 this.sc.addCACertificateToMap(cert, id, label); 493 } 494 495 496 497 /** 498 * Delete CA certificate with given label 499 * 500 * @param {String} label the label of certificate to be removed 501 */ 502 HSMKeyStore.prototype.deleteCACertificate = function(label) { 503 var cert = this.sc.getCACertificate(label); 504 505 if (!cert) { 506 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a certificate with label " + label); 507 } 508 509 var fid = ByteString.valueOf((SmartCardHSM.CERTDPREFIX << 8) + cert.id); 510 this.sc.deleteFile(fid); 511 var fid = ByteString.valueOf((SmartCardHSM.CACERTIFICATEPREFIX << 8) + cert.id); 512 this.sc.deleteFile(fid); 513 this.sc.enumerateCACertificates(); 514 } 515 516 517 518 /** 519 * Delete key and certificate with given label 520 * 521 * @param {String/Number/Key} labelOrIdOrKey the label, id or object of the key to be removed 522 */ 523 HSMKeyStore.prototype.deleteKey = function(labelOrIdOrKey) { 524 var key = labelOrIdOrKey; 525 if (!(key instanceof SmartCardHSMKey)) { 526 key = this.getKey(labelOrIdOrKey); 527 } 528 529 if (key) { 530 var kid = key.getId(); 531 } else { 532 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label " + labelOrIdOrKey); 533 } 534 535 var fid = ByteString.valueOf((SmartCardHSM.KEYPREFIX << 8) + kid); 536 this.sc.deleteFile(fid); 537 538 try { 539 var fid = ByteString.valueOf((SmartCardHSM.PRKDPREFIX << 8) + kid); 540 this.sc.deleteFile(fid); 541 542 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 543 this.sc.deleteFile(fid); 544 } 545 catch(e) { 546 // Ignore 547 } 548 549 this.enumerateKeys(); 550 } 551 552 553 554 /** 555 * Return list of keys 556 * 557 * @type String[] 558 * @return the list of key names 559 */ 560 HSMKeyStore.prototype.enumerateKeys = function() { 561 return this.sc.enumerateKeys(); 562 } 563 564 565 566 /** 567 * Check if key with label exists 568 * 569 * @param {String / Number} the key label or id 570 * @type Boolean 571 * @return true if key exists 572 */ 573 HSMKeyStore.prototype.hasKey = function(labelOrId) { 574 var key = this.sc.getKey(labelOrId); 575 return key != null; 576 } 577 578 579 580 /** 581 * Get key for given label 582 * 583 * @param {String / Number} the key label or id 584 * @type Key 585 * @return the key 586 */ 587 HSMKeyStore.prototype.getKey = function(labelOrId) { 588 var key = this.sc.getKey(labelOrId); 589 if (!key) { 590 throw new GPError("HSMKeyStore", GPError.INVALID_DATA, 0, "Could not find a key with label/id " + labelOrId); 591 } 592 return key; 593 } 594 595 596 597 /** 598 * Check if key has a certificate 599 * 600 * @param {String/Number/Key} labelOrIdOrKey the certificate label, id or key 601 * @type Boolean 602 * @return true of a certificate is present 603 */ 604 HSMKeyStore.prototype.hasCertificate = function(labelOrIdOrKey) { 605 var key = labelOrIdOrKey; 606 if (!(key instanceof SmartCardHSMKey)) { 607 key = this.getKey(labelOrIdOrKey); 608 } 609 var kid = key.getId(); 610 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 611 return this.sc.hasFile(fid); 612 } 613 614 615 616 /** 617 * Get raw certificate for given label 618 * 619 * @param {String/Number/Key} labelOrIdOrKey the certificate label, id or key 620 * @type ByteString 621 * @return the certificate 622 */ 623 HSMKeyStore.prototype.getCertificate = function(labelOrIdOrKey) { 624 var key = labelOrIdOrKey; 625 if (!(key instanceof SmartCardHSMKey)) { 626 key = this.getKey(labelOrIdOrKey); 627 } 628 var kid = key.getId(); 629 var fid = ByteString.valueOf((SmartCardHSM.EECERTIFICATEPREFIX << 8) + kid); 630 var certbin = this.sc.readBinary(fid); 631 return certbin; 632 } 633 634 635 636 /** 637 * Get certificate for given label 638 * 639 * @param {String/Number/Key} labelOrIdOrKey the certificate label, id or key 640 * @type X509 641 * @return the certificate 642 */ 643 HSMKeyStore.prototype.getEndEntityCertificate = function(labelOrIdOrKey) { 644 var certbin = this.getCertificate(labelOrIdOrKey); 645 return new X509(certbin); 646 } 647