asn1js: a JavaScript decoder for ASN.1 structures
JavaScript generic ASN.1 parser
At a glance
- What is it?
- asn1js decodes any valid ASN.1 structure in DER or BER encoding, and it does it in three shapes: an npm package for Node, a page that runs in a browser, and a single file you can save and open locally. The interesting engineering is in the edges, because the browser will not let a module load from a local file, and the tool works around that with a bundle and a set of pinned dependency overrides.
- Who is it for?
- asn1js fits a developer who needs to look inside a certificate, a CRL, a CMS blob, or an LDAP message, and who wants to do it in a browser without a native ASN.1 library, or in Node without a build step. It does not fit a project that needs a strict DER validator rather than a decoder, since the stated goal is to decode what is valid rather than to reject what is not.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 79 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A decoder, not a validator
asn1js is a generic ASN.1 parser and decoder, and the claim in the readme is that it can decode any valid ASN.1 DER or BER structure.
The two encodings matter. DER is the distinguished encoding rules, which is what you get when a certificate is signed, and it is canonical: one structure, one encoding. BER is the basic encoding rules, which is what a lot of protocols actually put on the wire, and it is more permissive in ways that a strict parser often rejects. A decoder that handles both is more useful than one that handles only the tidy form, because files in the wild are frequently in the messy one.
The tool is the kind that gets pointed at a file to find out what is inside it. A certificate, a certificate revocation list, a CMS signed message, an LDAP message, a timestamp request, a PKCS key structure. All of those are ASN.1 in disguise, and all of them are in the examples directory.
The implementation is JavaScript with no runtime dependencies, which is the property that makes the browser and the command line routes possible. The published package contains eight JavaScript files and nothing else, and the entry point is the decoder itself.
The project is ISC licensed, the default branch is trunk, the last push was on 2026-08-05, and there are no tagged releases, so the version to look at is the one in the package metadata, which is 2.1.3.
Three ways to load it, and one browser constraint that shapes everything
The usage section covers Node, the web, and local files, and the third of those is the interesting one because of a hard browser limitation.
In Node, the package installs with npm, pnpm, or yarn, and the classes are named imports.
import { ASN1 } from '@lapo/asn1js';The helper modules are separate entry points, so a project that only needs to turn hexadecimal into bytes imports a second path rather than pulling in everything.
import { Hex } from '@lapo/asn1js/hex.js';For code that is still CommonJS, the readme points out that since Node 22 the module can be required normally, with the experimental require module flag, and gives the two line form of that, ending with a call that decodes a hexadecimal object identifier and prints its content.
On older Node versions, the same code has to be written with an async import inside a function, and the readme shows that form too. The difference between the two examples is small, and the readme is honest that it is a version constraint rather than a preference.
In a browser, the import comes from a content delivery network by version, and the example sets the text of the page to the decoded content.
<script>
import { ASN1 } from 'https://unpkg.com/@lapo/[email protected]/asn1.js';
import { Hex } from 'https://unpkg.com/@lapo/[email protected]/hex.js';
document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
</script>And then the local case, which is where the browser gets in the way. ES modules do not work over the file protocol because of cross origin restrictions, so the project ships a bundled single file version that does work locally. It does not work online, because of the content security policy restrictions on inline content, which is an odd pair of constraints: the offline build cannot be served, and the online build cannot be opened from disk.
That is why there is a downloadable archive of the project as well as the online site.
The command line, and a corpus of real files
There is a command line entry point, and the readme notes that you do not even need to install the package to use it.
npx @lapo/asn1js ed25519.cerThe package manifest registers the same tool under a shorter command name, pointing at the dump script, and the file list in the manifest shows that the tool ships as a set of plain modules with no build step in between.
The examples directory is the part worth browsing. It contains the kind of files people actually want to look inside: an Ed25519 certificate with its decoded dump beside it, a certificate revocation list in both forms, a CMS message with a password, a signed message in both BER and DER encodings with a dump of the DER one, an LDAP message, a certificate from a well known certificate authority, a PKCS key in its first, eighth, and tenth versions, and a timestamp request. Several of them come in pairs, the raw file and the dump, which is how you check the decoder against a known good output.
Having a BER and a DER version of the same signed message in the directory is the kind of detail that tells you how the project is tested. The two encodings describe the same structure, and a decoder that produces the same tree from both is doing something more than pattern matching.
The examples also cover the file formats the tool accepts on input, because the readme says the page decodes base64 encoded input, with raw base64, PEM armoring, and the marker form recognised, as well as hexadecimal input, and local files in some browsers.
The identifier tables, and how they are kept current
A decoder that can only show you that a field is an object identifier is not much use, so the project carries the data that turns a number into a name.
The object identifier values are recognised using data taken from an existing program by Peter Gutmann, which is a sensible way to get a table of a few thousand values without typing them in.
The other side of that is the definition tables, and the repository has separate modules for the object identifier list, the definitions, and the definitions that come from the request for comments documents. There is a module for parsing those documents, and a shell script for updating the object identifier data, and another for updating the request for comments data.
So the tables are generated rather than hand maintained, and there are two update scripts because there are two sources. That is a maintenance design, and it is the part of the project that decides whether the names stay correct over the years, since a table of identifiers that is never updated becomes worse than no table at all.
The tags are in their own module, which is where the universal tag numbers are, and the context module holds the context specific tags, which is the part of ASN.1 that distinguishes an application tag from a context specific one.
The manifest publishes those modules as first class files, which means a consumer can import the tag table directly rather than reaching into the internals of the decoder.
The credit list, and what it says about the project
The credits section is worth reading on its own, because it describes a project that has been running for a long time and has been kept alive by other people.
The copyright notice runs from 2008, and the current year, so this is a project in its second decade. What has changed in that time is the list of contributors.
BMPString support was added by one contributor, extended tag support by another, relative object identifier support by a third. There are patches from two more, and the dark mode and other interface improvements by another. The readme lists each of them by name and by project, which is a good habit in a project with that many years of history.
There is also a link to a tracker hosted on the author's own infrastructure, which the readme marks as currently offline, and links to the official website, an alternate website, the single file version, a GitHub mirror, the changelog, and code statistics.
Two things follow from that list. The first is that the project is maintained in the way open source projects with a long life tend to be maintained: the original author keeps the core, and features arrive as pull requests from people who needed them. The second is that the credit for the parts a user touches, the dark mode, the string support, the relative identifiers, is not the author's alone, which is a useful thing to know if you are considering contributing.
The licence is the ISC one, which is short and permissive, and it comes with the usual disclaimer of warranties.
The build, the tooling, and the dependency overrides
The manifest is small, and the interesting parts are the tooling choices.
The package is an ES module with a minimum Node version of 14.6, the package manager is pinned to a specific pnpm version, and the scripts are four: the linter runs over the list of source files, the build is a Vite build, the test command runs a test file directly with Node, and a second command runs the definitions test. There is also a command that serves the project locally with a small static server, and a workflow validation command that checks the action files.
The development dependencies are few and tell the story of the browser constraints. There is Vite, a plugin that renders the DOM, and a plugin that produces a single file bundle, and that last one is the answer to the local file problem described earlier.
The overrides are the part to look at twice. The Rollup package is overridden to the WebAssembly build of itself, at the manifest level and in the pnpm overrides as well, and there is a second override that forces a version of the bundler tool for older ranges. Both of those are the kind of workaround that only makes sense if you know the reason, and the readme does not explain them, so they are worth a look before you start debugging a build.
The repository also carries two shell scripts besides the release one, one of which is a check script, and there are the two update scripts for the identifier tables. The visual side of the project is in the theme module and the two tree icons, one light and one dark, and the two favicons, one of which is the source for the other.
So the project is a decoder, a web front end for it, a command line dumper, and a set of generated reference tables, all in plain JavaScript with no runtime dependencies, which is the property that makes the three usage routes work.
Editorial conclusion
asn1js fits a developer who needs to look inside a certificate, a CRL, a CMS blob, or an LDAP message, and who wants to do it in a browser without a native ASN.1 library, or in Node without a build step. It does not fit a project that needs a strict DER validator rather than a decoder, since the stated goal is to decode what is valid rather than to reject what is not. Verify first that the file you have is one the parser expects, in particular whether it is base64, PEM armoured, or hexadecimal, since the input format is detected rather than declared, and that the browser route you need is the online page or the saved single file, because a module cannot be loaded from the local file system in a browser.
Frequently asked questions
What does asn1js decode?
Any valid ASN.1 structure in DER or BER encoding. It is a generic parser and decoder, so it handles certificates, certificate revocation lists, CMS signed messages, LDAP messages, and key structures, and it can take its input as raw base64, PEM armoured base64, or hexadecimal.
How do I install and use asn1js in Node?
Install the package with npm, pnpm, or yarn, then import the decoder class and, if you need it, the hexadecimal helper from its own module path. On Node 22 and later the module can be required directly with the experimental require module flag, and on older versions the import has to be asynchronous.
Why does asn1js ship a single file version that only works offline?
Because ES modules cannot be loaded over the file protocol in a browser, due to cross origin restrictions, so a bundled single file version is provided for local use. It cannot be served from a site, because of the content security policy restrictions on inline content, so the project publishes both an online site and a downloadable archive.
How do I decode an ASN.1 file from the command line with asn1js?
With the npx command against the package, for example npx with the package name and a file name, and the readme notes that nothing has to be installed first. The same tool is registered in the package manifest under a shorter command name pointing at the dump script.
How does asn1js know the names of object identifiers and tags?
The object identifier values are recognised using data from an existing program by Peter Gutmann, and the definition tables come from separate modules with their own update scripts, so the data is generated rather than typed in by hand. The tags and the context specific tags live in their own modules as well.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lapo-luchini-asn1js)Was this useful?