x509certificategenerator.js
Summary
A X509 certificate generator class following RFC5280
if (typeof(__ScriptingServer) == "undefined") {
load("pkixcommon.js");
}
function X509CertificateGenerator(crypto) {
this.crypto = crypto;
this.encodeECDomainParameter = true;
this.reset();
}
X509CertificateGenerator.prototype.reset = function() {
this.extensions = new Array();
}
X509CertificateGenerator.prototype.setSerialNumber = function(serialNumber) {
this.serialNumber = serialNumber;
}
X509CertificateGenerator.prototype.setIssuer = function(issuer) {
this.issuer = issuer;
}
X509CertificateGenerator.prototype.setNotBefore = function(date) {
this.notBefore = date;
}
X509CertificateGenerator.prototype.setNotAfter = function(date) {
this.notAfter = date;
}
X509CertificateGenerator.prototype.setSubject = function(subject) {
this.subject = subject;
}
X509CertificateGenerator.prototype.setPublicKey = function(publicKey) {
this.publicKey = publicKey;
}
X509CertificateGenerator.prototype.setSignatureAlgorithm = function(alg) {
this.signatureAlgorithm = alg;
}
X509CertificateGenerator.prototype.addExtension = function(extnID, critical, extnValue) {
var t = new ASN1("extension", ASN1.SEQUENCE,
new ASN1("extnID", ASN1.OBJECT_IDENTIFIER, new ByteString(extnID, OID))
);
if (critical) {
t.add(new ASN1("critical", ASN1.BOOLEAN, new ByteString("FF", HEX)));
}
t.add(new ASN1("extnValue", ASN1.OCTET_STRING, extnValue));
this.extensions.push(t);
}
X509CertificateGenerator.prototype.addKeyUsageExtension = function(flags) {
var t = new ASN1(ASN1.BIT_STRING, PKIXCommon.bitstringForInteger(flags));
this.addExtension("2.5.29.15", true, t.getBytes());
}
X509CertificateGenerator.digitalSignature = 0x0080;
X509CertificateGenerator.nonRepudiation = 0x0040;
X509CertificateGenerator.keyEncipherment = 0x0020;
X509CertificateGenerator.dataEncipherment = 0x0010;
X509CertificateGenerator.keyAgreement = 0x0008;
X509CertificateGenerator.keyCertSign = 0x0004;
X509CertificateGenerator.cRLSign = 0x0002;
X509CertificateGenerator.encipherOnly = 0x0001;
X509CertificateGenerator.decipherOnly = 0x8000;
X509CertificateGenerator.prototype.addBasicConstraintsExtension = function(cA, pathLenConstraint) {
var t = new ASN1("BasicConstraints",ASN1.SEQUENCE);
if (cA) {
t.add(new ASN1("cA", ASN1.BOOLEAN, new ByteString("FF", HEX)));
}
if (pathLenConstraint >= 0) {
var bb = new ByteBuffer();
bb.append(pathLenConstraint);
t.add(new ASN1("pathLenConstraint", ASN1.INTEGER, bb.toByteString()));
}
this.addExtension("2.5.29.19", true, t.getBytes());
}
X509CertificateGenerator.prototype.addSubjectKeyIdentifierExtension = function() {
var spi = this.getSubjectPublicKeyInfo();
var keyvalue = spi.get(1).value.bytes(1);
var crypto = new Crypto();
var hash = crypto.digest(Crypto.SHA_1, keyvalue);
var t = new ASN1(ASN1.OCTET_STRING, hash);
this.addExtension("2.5.29.14", false, t.getBytes());
}
X509CertificateGenerator.prototype.addAuthorityKeyIdentifierExtension = function(publicKey) {
if (publicKey.getComponent(Key.MODULUS)) {
var spi = PKIXCommon.createRSASubjectPublicKeyInfo(publicKey);
} else {
var spi = PKIXCommon.createECSubjectPublicKeyInfo(publicKey, this.encodeECDomainParameter);
}
var keyvalue = spi.get(1).value.bytes(1);
var crypto = new Crypto();
var hash = crypto.digest(Crypto.SHA_1, keyvalue);
var t = new ASN1(ASN1.SEQUENCE,
new ASN1(0x80, hash)
);
this.addExtension("2.5.29.35", false, t.getBytes());
}
X509CertificateGenerator.prototype.addCRLDistributionPointURL = function(url) {
var t = new ASN1("cRLDistributionPoints", ASN1.SEQUENCE);
for (var i = 0; i < url.length; i++) {
t.add( new ASN1("cRLDistributionPoint", ASN1.SEQUENCE,
new ASN1("distributionPoint", 0xA0,
new ASN1("fullName", 0xA0,
new ASN1("uniformResourceIdentifier", 0x86, new ByteString(url[i], ASCII))
)
)
));
}
this.addExtension("id-ce-cRLDistributionPoints", false, t.getBytes());
}
X509CertificateGenerator.prototype.addExtendedKeyUsages = function(oids, critical) {
var t = new ASN1("extKeyUsages", ASN1.SEQUENCE);
for (var i = 0; i < oids.length; i++) {
t.add( new ASN1("keyPurposeId", ASN1.OBJECT_IDENTIFIER, new ByteString(oids[i], OID) ));
}
this.addExtension("id-ce-extKeyUsage", critical, t.getBytes());
}
X509CertificateGenerator.prototype.getIssuer = function() {
if (this.issuer instanceof ASN1) {
return this.issuer;
} else {
return PKIXCommon.encodeName(this.issuer);
}
}
X509CertificateGenerator.prototype.getValidity = function() {
var t = new ASN1("validity", ASN1.SEQUENCE);
var ts = this.notBefore;
if (typeof(ts) != "string") {
ts = PKIXCommon.dtoUTC(this.notBefore);
}
t.add(new ASN1("notBefore", ASN1.UTCTime, new ByteString(ts, ASCII)));
var ts = this.notAfter;
if (typeof(ts) != "string") {
ts = PKIXCommon.dtoUTC(this.notAfter);
}
t.add(new ASN1("notAfter", ASN1.UTCTime, new ByteString(ts, ASCII)));
return t;
}
X509CertificateGenerator.prototype.getSubject = function() {
if (this.subject instanceof ASN1) {
return this.subject;
} else {
return PKIXCommon.encodeName(this.subject);
}
}
X509CertificateGenerator.prototype.getSubjectPublicKeyInfo = function() {
if (this.publicKey.getComponent(Key.MODULUS)) {
return PKIXCommon.createRSASubjectPublicKeyInfo(this.publicKey);
} else {
return PKIXCommon.createECSubjectPublicKeyInfo(this.publicKey, this.encodeECDomainParameter);
}
}
X509CertificateGenerator.prototype.getExtensions = function() {
var t = new ASN1("extensions", 0xA3);
var s = new ASN1("extensions", ASN1.SEQUENCE);
t.add(s);
for (var i = 0; i < this.extensions.length; i++) {
s.add(this.extensions[i]);
}
return t;
}
X509CertificateGenerator.prototype.getTbsCertificate = function() {
var t = new ASN1("tbsCertificate", ASN1.SEQUENCE);
t.add(new ASN1("version", 0xA0,
new ASN1("version", ASN1.INTEGER, new ByteString("02", HEX))));
t.add(new ASN1("serialNumber", ASN1.INTEGER, PKIXCommon.convertUnsignedInteger(this.serialNumber)));
t.add(this.getSignatureAlgorithm());
t.add(this.getIssuer());
t.add(this.getValidity());
t.add(this.getSubject());
t.add(this.getSubjectPublicKeyInfo());
t.add(this.getExtensions());
return t;
}
X509CertificateGenerator.prototype.getSignatureAlgorithm = function() {
var t = new ASN1("signatureAlgorithm", ASN1.SEQUENCE);
if (this.signatureAlgorithm == Crypto.RSA) {
t.add(new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, new ByteString("1.2.840.113549.1.1.5", OID)));
t.add(new ASN1("parameters", ASN1.NULL, new ByteString("", HEX)));
} else if (this.signatureAlgorithm == Crypto.RSA_SHA256) {
t.add(new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, new ByteString("1.2.840.113549.1.1.11", OID)));
t.add(new ASN1("parameters", ASN1.NULL, new ByteString("", HEX)));
} else if (this.signatureAlgorithm == Crypto.ECDSA_SHA256) {
t.add(new ASN1("algorithm", ASN1.OBJECT_IDENTIFIER, new ByteString("ecdsa-with-SHA256", OID)));
t.add(new ASN1("parameters", ASN1.NULL, new ByteString("", HEX)));
} else {
throw new GPError("X509CertificateGenerator", GPError.INVALID_MECH, this.signatureAlgorithm, "Invalid algorithm");
}
return t;
}
X509CertificateGenerator.prototype.generateX509Certificate = function(privateKey) {
var certificate = new ASN1("certificate", ASN1.SEQUENCE);
var tbs = this.getTbsCertificate();
certificate.add(tbs);
certificate.add(this.getSignatureAlgorithm());
var signature = this.crypto.sign(privateKey, this.signatureAlgorithm, tbs.getBytes());
signature = (new ByteString("00", HEX)).concat(signature);
var signatureValue = new ASN1("signatureValue", ASN1.BIT_STRING, signature);
certificate.add(signatureValue);
return new X509(certificate.getBytes());
}
function X509CertificateGeneratorRSATest() {
var crypto = new Crypto();
var caPrivateKey = new Key();
caPrivateKey.setType(Key.PRIVATE);
var caPublicKey = new Key();
caPublicKey.setType(Key.PUBLIC);
caPublicKey.setSize(1024);
crypto.generateKeyPair(Crypto.RSA, caPublicKey, caPrivateKey);
var x = new X509CertificateGenerator(crypto);
x.reset();
x.setSerialNumber(new ByteString("01", HEX));
x.setSignatureAlgorithm(Crypto.RSA);
var issuer = { C:"UT", O:"ACME Corporation", CN:"Test-CA" };
x.setIssuer(issuer);
x.setNotBefore("060825120000Z");
x.setNotAfter("160825120000Z");
var subject = { C:"UT", O:"Utopia CA", OU:"ACME Corporation", CN:"Joe Doe" };
x.setSubject(subject);
x.setPublicKey(caPublicKey);
x.addKeyUsageExtension( PKIXCommon.digitalSignature |
PKIXCommon.keyCertSign |
PKIXCommon.cRLSign );
x.addBasicConstraintsExtension(true, 0);
x.addSubjectKeyIdentifierExtension();
x.addAuthorityKeyIdentifierExtension(caPublicKey);
var cert = x.generateX509Certificate(caPrivateKey);
var fn = GPSystem.mapFilename("cert_rsa.cer", GPSystem.USR);
PKIXCommon.writeFileToDisk(fn, cert.getBytes());
cert.verifyWith(cert);
print(cert);
}
function X509CertificateGeneratorECCTest() {
var crypto = new Crypto();
var caPrivateKey = new Key();
caPrivateKey.setType(Key.PRIVATE);
var caPublicKey = new Key();
caPublicKey.setType(Key.PUBLIC);
caPublicKey.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
crypto.generateKeyPair(Crypto.EC, caPublicKey, caPrivateKey);
var x = new X509CertificateGenerator(crypto);
x.reset();
x.setSerialNumber(new ByteString("01", HEX));
x.setSignatureAlgorithm(Crypto.ECDSA_SHA256);
var issuer = { C:"UT", O:"ACME Corporation", CN:"Test-CA" };
x.setIssuer(issuer);
var t = new Date();
x.setNotBefore(t);
x.setNotAfter(PKIXCommon.addDays(t, 180));
var subject = { C:"UT", O:"Utopia CA", OU:"ACME Corporation", CN:"Joe Doe" };
x.setSubject(subject);
x.setPublicKey(caPublicKey);
x.addKeyUsageExtension( PKIXCommon.digitalSignature |
PKIXCommon.keyCertSign |
PKIXCommon.cRLSign );
x.addBasicConstraintsExtension(true, 0);
x.addSubjectKeyIdentifierExtension();
x.addAuthorityKeyIdentifierExtension(caPublicKey);
var cert = x.generateX509Certificate(caPrivateKey);
var fn = GPSystem.mapFilename("cert_ecc.cer", GPSystem.USR);
PKIXCommon.writeFileToDisk(fn, cert.getBytes());
cert.verifyWith(cert);
print(cert);
print(new ASN1(cert.getBytes()));
}
Documentation generated by
JSDoc on Tue Apr 15 22:10:49 2025