1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2009 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 A Signer backed by a X.509 certificate 25 */ 26 27 var Holder = require('scsh/pki-db/Holder').Holder; 28 var Certificate = require('scsh/pki-db/Certificate').Certificate; 29 var PKIXCommon = require('scsh/x509/PKIXCommon').PKIXCommon; 30 var PKCS10 = require('scsh/pkcs/PKCS10').PKCS10; 31 var PKCS10Generator = require('scsh/x509/PKCS10Generator').PKCS10Generator; 32 var LongDate = require('scsh/pki-db/LongDate').LongDate; 33 34 35 36 /** 37 * Create a signer based on a X.509 certificate 38 * 39 * @class Class implementing a signer backed by a X.509 certificate 40 * @constructor 41 * @param {DAOFactory} daof the factory that can create the required data access objects 42 * @param {CryptoProviderFactory} cpf factory implementing getCryptoProvider() used to get access to crypto providers 43 * @param {Holder} holder the holder object for this signer 44 */ 45 function X509Signer(daof, cpf, holder) { 46 this.daof = daof; 47 this.cpf = cpf; 48 this.holder = holder; 49 50 if (this.holder == null) { 51 throw new GPError(module.id, GPError.INVALID_DATA, 1, "Certificate holder " + holder + " not found"); 52 } 53 54 if (this.getSigner()) { 55 this.parsePolicyFromSigner(); 56 } 57 } 58 59 exports.X509Signer = X509Signer; 60 61 62 63 /** 64 * Create a new signer 65 * 66 * @param {DAOFactory} daof the factory that can create the required data access objects 67 * @param {String/Number} pathOrHolderId the path of holderIDs (eg. "/UTCVCA/UTDVCA/UTTERM") or the holderId from the database 68 * @param {Number} certtype optional argument, default Holder.X509 69 * @param {Object} template template for database entry 70 * @type Number 71 * @return the newly created holder id 72 */ 73 X509Signer.createSigner = function(daof, pathOrHolderId, certtype, template) { 74 if (typeof(certtype) == "undefined") { 75 certtype = Holder.X509; 76 } 77 78 var holderdao = daof.getHolderDAO(); 79 if (typeof(pathOrHolderId) == "string") { 80 var holder = holderdao.newHolder(pathOrHolderId, certtype, template); 81 } else { 82 if (typeof(pathOrHolderId) == "undefined") { 83 pathOrHolderId = 0; 84 } 85 86 var holder = holderdao.newHolderForParent(pathOrHolderId, certtype, template); 87 } 88 89 return holder.id; 90 } 91 92 93 94 /** 95 * Set policy for signer object. 96 * 97 * The policy object shall contain the following properties 98 * 99 * <ul> 100 * <li>distinguishedName - The distinguishedName object as defined in PKIXCommon.encodeName()</li> 101 * <li>keySpecification - A Key object initialized with the key parameter.</li> 102 * <li>signatureAlgorithm - A ByteString encoding the object identifier for the signature algorithm</li> 103 * <li>validityDaysSelfSigned - Number of days the self-signed certificate is valid</li> 104 * <li>validityDays - Number of days the issued certificate is valid</li> 105 * <li>pathLenConstraint - Number of subordinate CAs</li> 106 * <li>requestFormat - "pkcs10" or "sc-hsm"</li> 107 * <li>overwriteKey - Set to true to overwrite a key with the same label</li> 108 * </ul> 109 * 110 * @see PKIXCommon.encodeName() 111 * @param {String} crldp the URL of the distribution point 112 */ 113 X509Signer.prototype.setPolicy = function(policy) { 114 this.policy = policy; 115 } 116 117 118 119 /** 120 * Determine signer name for newly generated signer 121 * 122 * @type String 123 * @return the unique name 124 */ 125 X509Signer.prototype.determineSignerName = function() { 126 var name = this.holder.id + ":X509Signer " + this.holder.signerNo + 1 + " [" + Date() + "]"; 127 var holderdao = this.daof.getHolderDAO(); 128 holderdao.updateSignerNo(this.holder, this.holder.signerNo + 1); 129 return name; 130 } 131 132 133 134 /** 135 * Determine subject distinguished name for new signer 136 * 137 * @param {String} name 138 * @type String 139 * @return the distringuished name for the new signer 140 */ 141 X509Signer.prototype.determineDistinguishedName = function(name) { 142 return this.policy.distinguishedName; 143 } 144 145 146 147 /** 148 * Determine the key usage for the request 149 * 150 * @type Number 151 * @return the key usage defined in PKIXCommon 152 */ 153 X509Signer.prototype.getRequestKeyUsage = function() { 154 return PKIXCommon.digitalSignature; 155 } 156 157 158 159 /** 160 * Create a new signer key pair 161 * 162 * @param {String} name the signer name 163 * @type ByteString 164 * @return the subject key identifier 165 */ 166 X509Signer.prototype.newSigner = function(name, template) { 167 if (!name) { 168 name = this.determineSignerName(); 169 } 170 171 if (!template || !template.keyDomain) { 172 throw new GPError(module.id, GPError.INVALID_DATA, 1, "Cannot create a signer without a template containing the keyDomain"); 173 } 174 175 var cp = this.cpf.getCryptoProvider(template.keyDomain, true); 176 try { 177 if (this.policy.overwriteKey) { 178 cp.deleteIfExists = true; 179 } 180 181 var k = cp.generateKeyPair(name, this.policy.keySpecification); 182 var prk = k.prk; 183 var puk = k.puk; 184 var req = k.req; 185 var keyblob; 186 187 var keyId = PKIXCommon.determineKeyIdentifier(puk); 188 189 if (typeof(cp.getWrappedKey) == "function") { 190 template.keyblob = cp.getWrappedKey(name); 191 } 192 193 var signerDAO = this.daof.getSignerDAO(); 194 this.signer = signerDAO.newSigner(this.holder, name, keyId, template); 195 196 if (this.policy.requestFormat != "sc-hsm") { 197 var gen = new PKCS10Generator(cp.getCrypto()); 198 gen.setSignatureAlgorithm(this.policy.reqSignatureAlgorithm); 199 gen.setSubject(this.determineDistinguishedName()); 200 gen.setPublicKey(puk); 201 gen.addKeyUsageExtension(this.getRequestKeyUsage()); 202 req = gen.generateCertificationRequest(prk); 203 req = req.getBytes(); 204 } 205 206 var requestDAO = this.daof.getRequestDAO(); 207 requestDAO.newRequest(this.holder, keyId, req); 208 } 209 finally { 210 cp.release(); 211 } 212 213 return keyId; 214 } 215 216 217 218 /** 219 * Return the holderId from the holder database for this element 220 * 221 * @type Number 222 * @return the holderId 223 */ 224 X509Signer.prototype.getHolderId = function() { 225 return this.holder.id; 226 } 227 228 229 230 /** 231 * Return the holder from the holder database for this element 232 * 233 * @type Number 234 * @return the holderId 235 */ 236 X509Signer.prototype.getHolder = function() { 237 return this.holder; 238 } 239 240 241 242 /** 243 * Return the signer from the signer database for this element 244 * 245 * @type Signer 246 * @return the signer value object or null 247 */ 248 X509Signer.prototype.getSigner = function() { 249 GPSystem.log(GPSystem.DEBUG, module.id, "getSigner()"); 250 if (this.signer) { 251 GPSystem.log(GPSystem.DEBUG, module.id, "return cached signer"); 252 return this.signer; 253 } 254 255 var certDAO = this.daof.getCertificateDAO(); 256 this.currentCertificate = certDAO.getCurrentCertificate(this.holder); 257 258 if (!this.currentCertificate) { // no active signer 259 GPSystem.log(GPSystem.DEBUG, module.id, "no active signer for holder " + this.holder.id); 260 return null; 261 } 262 263 var signerDAO = this.daof.getSignerDAO(); 264 this.signer = signerDAO.getSignerByKeyId(this.holder, this.currentCertificate.keyId); 265 266 if (this.signer) { 267 this.cpid = this.signer.keyDomain; 268 } 269 270 return this.signer; 271 } 272 273 274 275 /** 276 * Parse the policy from the signer's values object 277 * 278 * @type Object 279 * @return the policy 280 */ 281 X509Signer.prototype.parsePolicyFromSigner = function() { 282 var signer = this.getSigner(); 283 284 if (signer == null) { 285 throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer"); 286 } 287 288 var c = signer.getContent(); 289 290 if (!c || !c.keySpecification) { 291 return; // no policy 292 } 293 294 var dp = new Key(); 295 if (c.keySpecification.type == "EC") { 296 dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.keySpecification.curve, OID)); 297 } else { 298 dp.setSize(c.keySpecification.keysize); 299 } 300 301 var dn = PKIXCommon.parseDN(c.distinguishedName); 302 303 this.policy = { 304 distinguishedName: dn, 305 signatureAlgorithm: c.signatureAlgorithm, 306 reqSignatureAlgorithm: c.keySpecification.sigalg, 307 validityDaysCertificates: c.validityDaysCertificates, 308 validityDaysCRL: c.validityDaysCRL, 309 pathLenConstraint: c.pathLenConstraint, 310 keySpecification: dp 311 } 312 313 return this.policy; 314 } 315 316 317 318 X509Signer.prototype.getSubjectPolicyForRequest = function() { 319 GPSystem.log(GPSystem.DEBUG, module.id, "getSubjectPolicyForRequest()"); 320 321 var signer = this.getSigner(); 322 323 if (signer == null) { 324 throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer"); 325 } 326 327 var c = signer.getContent(); 328 329 var dp = new Key(); 330 if (c.keySpecification.type == "EC") { 331 dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.keySpecification.curve, OID)); 332 } else { 333 dp.setSize(c.keySpecification.keysize); 334 } 335 336 var policy = { 337 reqSignatureAlgorithm: c.keySpecification.sigalg, 338 keySpecification: dp 339 } 340 341 return policy; 342 } 343 344 345 346 /** 347 * Get request for the given subject key identifier 348 * 349 * @param {ByteString} keyId the subject key identifier 350 * @type ByteString 351 * @return the raw request 352 */ 353 X509Signer.prototype.getRequestBinary = function(keyId) { 354 var requestDAO = this.daof.getRequestDAO(); 355 var request = requestDAO.getRequestByKeyId(this.holder, keyId); 356 357 if (request == null) { 358 return null; 359 } 360 361 return request.bytes; 362 } 363 364 365 366 /** 367 * Get request for the given subject key identifier 368 * 369 * @param {ByteString} keyId the subject key identifier 370 * @type PKCS10 371 * @return the PKCS10 request 372 */ 373 X509Signer.prototype.getRequest = function(keyId) { 374 var requestDAO = this.daof.getRequestDAO(); 375 var request = requestDAO.getRequestByKeyId(this.holder, keyId); 376 377 if (request == null) { 378 return null; 379 } 380 381 var req = new PKCS10(request.bytes); 382 return req; 383 } 384 385 386 387 /* 388 X509Signer.prototype.activateSigner = function(name) { 389 } 390 391 392 393 X509Signer.prototype.importCertificate = function(name) { 394 } 395 396 397 398 X509Signer.prototype.getSignerList = function(usableOnly) { 399 } 400 */ 401 402 403 404 /** 405 * Store a certificate issued for a certain holder 406 * 407 * @param {X509} cert the certificate 408 * @param {Boolean} makeCurrent true if this certificate becomes the current certificate 409 * @param {ByteString} keyId the key id that links this certificate to the signer (usually the subjectKeyIdentifier) 410 * @param {Number} srId service request id 411 * @type Number 412 * @return the database id of the certificate 413 */ 414 X509Signer.prototype.storeCertificateForHolder = function(holder, cert, makeCurrent, keyId, srId) { 415 GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificateForHolder(" + holder + "'" + cert + "'," + makeCurrent + "," + keyId + ")"); 416 417 var certdao = this.daof.getCertificateDAO(); 418 419 var issuer = cert.getIssuerDNString(); 420 var subject = cert.getSubjectDNString(); 421 var autid = cert.getAuthorityKeyIdentifier(); 422 var subid = cert.getSubjectKeyIdentifier(); 423 var serial = cert.getSerialNumber().toString(HEX); 424 425 if ((autid && autid.equals(subid)) || issuer.equals(subject)) { 426 var dir = Certificate.SHORT; 427 } else { 428 var dir = Certificate.UP; 429 } 430 431 var certificate = certdao.getCertificateBySerial(holder, serial, dir); 432 433 if (certificate) { 434 GPSystem.log(GPSystem.INFO, module.id, "storeCertificate() : We already have a different certificate for that serial number"); 435 GPSystem.log(GPSystem.INFO, module.id, "Existing: " + (new X509(certificate.bytes))); 436 GPSystem.log(GPSystem.INFO, module.id, "New : " + cert); 437 438 return; 439 } 440 441 if (typeof(keyId) == "undefined") { 442 keyId = PKIXCommon.determineKeyIdentifier(cert.getPublicKey()); 443 } 444 445 var template = { 446 keyId: keyId, 447 expiry: new LongDate(cert.getNotAfter()) 448 }; 449 450 if (srId) { 451 template.serviceRequestId = srId; 452 } 453 454 var certificate = certdao.newCertificate(holder, serial, dir, cert.getBytes(), template); 455 456 if (makeCurrent || !holder.certId) { 457 var holderdao = this.daof.getHolderDAO(); 458 holderdao.updateCurrentCertificate(holder, certificate); 459 } 460 461 return certificate.id; 462 } 463 464 465 466 /** 467 * Store a certificate issued for this signer 468 * 469 * @param {X509} cert the certificate 470 * @param {Boolean} makeCurrent true if this certificate becomes the current certificate 471 * @param {ByteString} keyId the key id that links this certificate to the signer (usually the subjectKeyIdentifier) 472 * @param {Number} srId service request id 473 */ 474 X509Signer.prototype.storeCertificate = function(cert, makeCurrent, keyId, srId) { 475 return this.storeCertificateForHolder(this.holder, cert, makeCurrent, keyId, srId); 476 } 477 478 479 480 /** 481 * Return the signer's certificate 482 * 483 * @type X509 484 * @return the signer's certificate 485 */ 486 X509Signer.prototype.getSignerCertificate = function() { 487 GPSystem.log(GPSystem.DEBUG, module.id, "getSignerCertificate()"); 488 if (!this.currentCertificate) { 489 this.getSigner(); 490 } 491 492 if (this.signer && !this.currentCertificate) { 493 var certDAO = this.daof.getCertificateDAO(); 494 this.currentCertificate = certDAO.getCurrentCertificate(this.holder); 495 } else if (!this.signer) { 496 GPSystem.log(GPSystem.DEBUG, module.id, "no active signer for holder " + this.holder.id); 497 return null; 498 } 499 500 return new X509(this.currentCertificate.bytes); 501 } 502