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