diff --git a/packages/node-opcua-crypto-test/test-fixtures/certs/strange-certificate.pem b/packages/node-opcua-crypto-test/test-fixtures/certs/strange-certificate.pem new file mode 100644 index 0000000..1924356 --- /dev/null +++ b/packages/node-opcua-crypto-test/test-fixtures/certs/strange-certificate.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIIZJvScHwEdR4wDQYJKoZIhvcNAQELBQAwYDEfMB0GCgmSJomT8ixkARkW +D0VDMkFNQVotNFFKR0RDTTELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB1Vua25vd24xHjAcBgNVBAMT +FU9QQy1Sb3V0ZXItTWlkZGxld2FyZTAeFw0yNDA5MjAxNjU1NDhaFw0zNDA5MjAxNjU1NDhaMGAx +HzAdBgoJkiaJk/IsZAEZFg9FQzJBTUFaLTRRSkdEQ00xCzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdV +bmtub3duMR4wHAYDVQQDExVPUEMtUm91dGVyLU1pZGRsZXdhcmUwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCsHj5qkQvHObJhVeaZBqSydJ2QpnI6kW7wC3oGrEu15k0BspYvBPU4mvh4 +fMU4vF3YSCMI85dmo+Urjuz0dEpYYVsSOtaMqyBpiFym50R3QgpeRPYsoohKiif2gopp1sraaLx6 +INybVETagw+ZskBjbVmFR2fwMe55DWL7qDqzpWQQGC5wSxPMOOhTB4adHj6JIw18y1uD4g1dFq7c +ZD0kK+XZ3o5dtBq+uEYZbyOugfUJMH5oAKZlUt9wCDGveBbqpOWoj6sd/0JMZ2t+6Bm5Pevf2jaU +D/Ba9RIckODMrjjD9a3RvddXGTb48ubQUI7iS7+mbqOMl0j/U2u4nJ6JAgMBAAGjgbswgbgwfwYD +VR0RBHgwdocEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYYkdXJuOkVDMkFNQVotNFFKR0RDTTppbnJh +eTpPUEMtUm91dGVyoCUGCisGAQQBgjcUAgOgFwwVT1BDLVJvdXRlci1NaWRkbGV3YXJlgg9FQzJB +TUFaLTRRSkdEQ00wCQYDVR0TBAIwADALBgNVHQ8EBAMCAvQwHQYDVR0lBBYwFAYIKwYBBQUHAwEG +CCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQB7An9+Ct9hM8LDz5xWXurBplrtUOeYZwuP8eDB +xuaQkh0yav3W+PqUplR9HYM6jzZFdttIHLwaefNmvteFicbe95nQZDl6kAZ3QhXFn8AKU+6zTbon +5SlWfGLOUUNDPSnwYY5lARytMmrlM9+8Lge5MUCPdMNmma67gqoNGhLAbXfjJcwRURH79XL66lBs +8unuvC1bgZHLMhATRg62EA3gJz21hDzA6NMfZG4xHLOzumXPJcCunz5P7PfBHsTvRB5ZEaZLRpRI +PEkqytds1IBzo/ZbgHuV67WzcKQHTHZtnUYLttpRKHB8kbgrPTUw8249RF0Gg9o5r+cZjr9MXvRt +-----END CERTIFICATE----- diff --git a/packages/node-opcua-crypto-test/test/.gitignore b/packages/node-opcua-crypto-test/test/.gitignore index a6c7c28..7d3de36 100644 --- a/packages/node-opcua-crypto-test/test/.gitignore +++ b/packages/node-opcua-crypto-test/test/.gitignore @@ -1 +1,3 @@ *.js +*.js.map + diff --git a/packages/node-opcua-crypto-test/test/test_crypto_explore_certificate.ts b/packages/node-opcua-crypto-test/test/test_crypto_explore_certificate.ts index ee39a18..c0d2f34 100644 --- a/packages/node-opcua-crypto-test/test/test_crypto_explore_certificate.ts +++ b/packages/node-opcua-crypto-test/test/test_crypto_explore_certificate.ts @@ -35,6 +35,8 @@ import { pemToPrivateKey, generatePrivateKeyFile, convertPEMtoDER, + exploreAsn1, + readCertificatePEM, } from "node-opcua-crypto"; describe(" exploring Certificates", function (this: Mocha.Suite) { @@ -185,6 +187,15 @@ describe(" exploring Certificates", function (this: Mocha.Suite) { // console.log(JSON.stringify(certificate_info, null, " ")); }); + it("AFS-01 investigate certificate with block problem 3", () => { + const filename = path.join(__dirname, "../test-fixtures/certs/strange-certificate.pem"); + const certificatePem = readCertificatePEM(filename); + const certificate = convertPEMtoDER(certificatePem); + exploreAsn1(certificate); + console.log(certificate.toString("base64")); + const certificate_info = exploreCertificate(certificate); + }); + it("PEC-1: investigate certificate generated by @peculiar/webcrypto", () => { const content = fs.readFileSync(path.join(__dirname, "../test-fixtures/peculiar_cert_in_base64.txt"), "utf-8"); const certificate = Buffer.from(content, "base64"); diff --git a/packages/node-opcua-crypto/source/asn1.ts b/packages/node-opcua-crypto/source/asn1.ts index ead6ed7..eeb373d 100644 --- a/packages/node-opcua-crypto/source/asn1.ts +++ b/packages/node-opcua-crypto/source/asn1.ts @@ -23,20 +23,27 @@ export enum TagType { BMPString = 0x1e, SEQUENCE = 0x30, + SET = 0x31, - A3 = 0xa3, + CONTEXT_SPECIFIC0 = 0xa0, + CONTEXT_SPECIFIC1 = 0xa1, + CONTEXT_SPECIFIC2 = 0xa2, + CONTEXT_SPECIFIC3 = 0xa3, + A4 = 0xa4 } export interface BlockInfo { tag: TagType | number; position: number; length: number; + start: number; } export function readTag(buf: Buffer, pos: number): BlockInfo { - assert(buf instanceof Buffer); - assert(Number.isFinite(pos) && pos >= 0); + + + const start = pos; // istanbul ignore next if (buf.length <= pos) { throw new Error("Invalid position : buf.length=" + buf.length + " pos =" + pos); @@ -56,7 +63,7 @@ export function readTag(buf: Buffer, pos: number): BlockInfo { pos += 1; } } - return { tag, position: pos, length }; + return { start, tag, position: pos, length }; } export function _readStruct(buf: Buffer, blockInfo: BlockInfo): BlockInfo[] { @@ -154,8 +161,8 @@ export function _readIntegerAsByteString(buffer: Buffer, block: BlockInfo): Buff export function _readListOfInteger(buffer: Buffer): Buffer[] { const block = readTag(buffer, 0); const inner_blocks = _readStruct(buffer, block); - return inner_blocks.map((bblock: BlockInfo) => { - return _readIntegerAsByteString(buffer, bblock); + return inner_blocks.map((innerBlock: BlockInfo) => { + return _readIntegerAsByteString(buffer, innerBlock); }); } diff --git a/packages/node-opcua-crypto/source/crypto_explore_certificate.ts b/packages/node-opcua-crypto/source/crypto_explore_certificate.ts index 49eab1f..a62909c 100644 --- a/packages/node-opcua-crypto/source/crypto_explore_certificate.ts +++ b/packages/node-opcua-crypto/source/crypto_explore_certificate.ts @@ -282,6 +282,8 @@ function _readGeneralNames(buffer: Buffer, block: BlockInfo) { 6: { name: "uniformResourceIdentifier", type: "IA5String" }, 7: { name: "iPAddress", type: "OCTET_STRING" }, 8: { name: "registeredID", type: "OBJECT_IDENTIFIER" }, + 32: { name: "otherName", type: "AnotherName" }, + }; const blocks = _readStruct(buffer, block); @@ -301,13 +303,32 @@ function _readGeneralNames(buffer: Buffer, block: BlockInfo) { // tslint:disable-next-line: no-bitwise const t = block.tag & 0x7f; const type = _data[t] as { name: string; type: string } | undefined; - // istanbul ignore next if (!type) { - throw new Error(" INVALID TYPE => " + t + "0x" + t.toString(16)); + console.log("_readGeneralNames: INVALID TYPE => " + t + " 0x" + t.toString(16)); + continue; + } + + if (t == 32) { + // console.log(buffer.subarray(block.start, block.position+ block.length).toString("hex")); + n[type.name] = n[type.name] || []; + + const blocks2 = _readStruct(buffer, block); + const name = _readObjectIdentifier(buffer, blocks2[0]).name; + const buf = _getBlock(buffer, blocks2[1]); + const b = readTag(buf, 0); + const nn = _readValue(buf, b); + // console.log(buf.toString("hex"), buf.toString("ascii")); + // console.log("name = ", name, nn); + const data = { + identifier: name, + value: nn, + }; + n[type.name].push(data.value); + } else { + n[type.name] = n[type.name] || []; + n[type.name].push(_readFromType(buffer, block, type.type)); } - n[type.name] = n[type.name] || []; - n[type.name].push(_readFromType(buffer, block, type.type)); } return n; } diff --git a/packages/node-opcua-crypto/source/explore_asn1.ts b/packages/node-opcua-crypto/source/explore_asn1.ts new file mode 100644 index 0000000..4319fd9 --- /dev/null +++ b/packages/node-opcua-crypto/source/explore_asn1.ts @@ -0,0 +1,34 @@ +import { BlockInfo, readTag, _readStruct, TagType } from "./asn1"; +import { hexDump } from "./crypto_utils"; + +function t(tag: number) { + // convert Asn1 tag to string + return TagType[tag]; +} +function bi(blockInfo: BlockInfo, depth: number) { + const indent = " ".repeat(depth); + const hl = blockInfo.position - blockInfo.start; // header length + return `${blockInfo.start.toString().padStart(5, " ")}:d=${depth} hl=${hl.toString().padEnd(3, " ")} l=${blockInfo.length + .toString() + .padStart(6, " ")} ${blockInfo.tag.toString(16).padEnd(2, " ")} ${indent} ${t(blockInfo.tag)}`; +} + +export function exploreAsn1(buffer: Buffer) { + console.log(hexDump(buffer)); + + function dump(offset: number, depth: number) { + const blockInfo = readTag(buffer, offset); + dumpBlock(blockInfo, depth); + + function dumpBlock(blockInfo: BlockInfo, depth: number) { + console.log(bi(blockInfo, depth)); + if (blockInfo.tag === TagType.SEQUENCE || blockInfo.tag === TagType.SET || blockInfo.tag >= TagType.CONTEXT_SPECIFIC0) { + const blocks = _readStruct(buffer, blockInfo); + for (const block of blocks) { + dumpBlock(block, depth +1); + } + } + } + } + dump(0, 0); +} diff --git a/packages/node-opcua-crypto/source/index.ts b/packages/node-opcua-crypto/source/index.ts index e88b97f..db6c6ab 100644 --- a/packages/node-opcua-crypto/source/index.ts +++ b/packages/node-opcua-crypto/source/index.ts @@ -41,4 +41,5 @@ export * from "./x509/create_self_signed_certificate.js"; export * from "./x509/coerce_private_key.js"; export * from "./subject.js"; export * from "./asn1.js"; +export * from "./explore_asn1.js"; export * from "./make_private_key_from_pem.js"; diff --git a/packages/node-opcua-crypto/source/oid_map.ts b/packages/node-opcua-crypto/source/oid_map.ts index 5b7becf..7351d59 100644 --- a/packages/node-opcua-crypto/source/oid_map.ts +++ b/packages/node-opcua-crypto/source/oid_map.ts @@ -66,6 +66,9 @@ export const oid_map: { [key: string]: { d: string; c: string; w?: boolean } } = "1.3.6.1.4.1.311.2.1.22": { d: "1.3.6.1.4.1.311.2.1.22", c: "SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID" }, "1.3.6.1.4.1.311.10.3.1": { d: "1.3.6.1.4.1.311.10.3.1", c: "Signer of CTLs -- szOID_KP_CTL_USAGE_SIGNING" }, "1.3.6.1.4.1.311.10.3.4": { d: "1.3.6.1.4.1.311.10.3.4", c: "szOID_EFS_RECOVERY (Encryption File System)" }, + + "1.3.6.1.4.1.311.20.2.3": { d: "1.3.6.1.4.1.311.20.2.3", c: "id-on-personalData" }, + "1.3.6.1.5.5.7.3.17": { d: "1.3.6.1.5.5.7.3.17", c: "Internet Key Exchange (IKE)" }, "1.3.6.1.5.5.7.3.1": { d: "serverAuth", c: "PKIX key purpose" },