diff --git a/convert.html b/convert.html
new file mode 100644
index 0000000..6bf410a
--- /dev/null
+++ b/convert.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+
Convert openSSH public key format to PEM
+
Form the command line you can run
+
+ ssh-keygen -f ~/.ssh/id_rsa -m 'PEM' -e > public.pem
+ in order to export your openssh public key to a PEM file, but doing in so in javascript is not too difficult and doesn't
+ require the webcrypto API, only a bit of ASN.1 knowledge.
+
Again
+ Converting open ssh public key proves to be invaluable to come up with the tight conversion
+
Result can be check with openssl asn.1 decoder
+
+ openssl asn1parse -i -in public.pem
+
+
No data is being sent to the server, everything happens within the context of this web page.
+
+
+
+
+
+ OpenSSH public Key:
+
+
+
+
Convert
+
+ PEM:
+
+
+
+
+
+
Made with
+
♥ by
+
Patrick Roumanoff
+
+
+
+
+
+
+
+
+
diff --git a/publicSshToPem.js b/publicSshToPem.js
new file mode 100644
index 0000000..3c68e7d
--- /dev/null
+++ b/publicSshToPem.js
@@ -0,0 +1,103 @@
+/* eslint no-bitwise: 0 */
+
+function wrap(text, len) {
+ const length = len || 72;
+ let result = "";
+ for (let i = 0; i < text.length; i += length) {
+ result += text.slice(i, i + length);
+ result += "\n";
+ }
+ return result;
+}
+
+function pemPublicKey(key) {
+ return `---- BEGIN RSA PUBLIC KEY ----\n${wrap(key, 65)}---- END RSA PUBLIC KEY ----`;
+}
+
+function integerToOctet(n) {
+ const result = [];
+ for (let i = n; i > 0; i >>= 8) {
+ result.push(i & 0xff);
+ }
+ return result.reverse();
+}
+
+function asnEncodeLen(n) {
+ let result = [];
+ if (n >> 7) {
+ result = integerToOctet(n);
+ result.unshift(0x80 + result.length);
+ } else {
+ result.push(n);
+ }
+ return result;
+}
+
+function checkHighestBit(v) {
+ if (v[0] >> 7 === 1) {
+ v.unshift(0); // add leading zero if first bit is set
+ }
+ return v;
+}
+
+function asn1Int(int) {
+ const v = checkHighestBit(int);
+ const len = asnEncodeLen(v.length);
+ return [0x02].concat(len, v); // int tag is 0x02
+}
+
+function asn1Seq(seq) {
+ const len = asnEncodeLen(seq.length);
+ return [0x30].concat(len, seq); // seq tag is 0x30
+}
+
+function arrayToPem(a) {
+ return window.btoa(a.map(c => String.fromCharCode(c)).join(""));
+}
+
+function arrayToString(a) {
+ return String.fromCharCode.apply(null, a);
+}
+
+function stringToArray(s) {
+ return s.split("").map(c => c.charCodeAt());
+}
+
+function pemToArray(pem) {
+ return stringToArray(window.atob(pem));
+}
+
+function arrayToLen(a) {
+ let result = 0;
+ for (let i = 0; i < a.length; i += 1) {
+ result = result * 256 + a[i];
+ }
+ return result;
+}
+
+function decodePublicKey(s) {
+ const split = s.split(" ");
+ const prefix = split[0];
+ if (prefix !== "ssh-rsa") {
+ throw new Error(`Unknown prefix: ${prefix}`);
+ }
+ const buffer = pemToArray(split[1]);
+ const nameLen = arrayToLen(buffer.splice(0, 4));
+ const type = arrayToString(buffer.splice(0, nameLen));
+ if (type !== "ssh-rsa") {
+ throw new Error(`Unknown key type: ${type}`);
+ }
+ const exponentLen = arrayToLen(buffer.splice(0, 4));
+ const exponent = buffer.splice(0, exponentLen);
+ const keyLen = arrayToLen(buffer.splice(0, 4));
+ const key = buffer.splice(0, keyLen);
+ return { type, exponent, key, name: split[2] };
+}
+
+function publicSshToPem(publicKey) {
+ const { key, exponent } = decodePublicKey(publicKey);
+ const seq = [key, exponent].map(asn1Int).reduce((acc, a) => acc.concat(a));
+ return pemPublicKey(arrayToPem(asn1Seq(seq)));
+}
+
+module.exports = { publicSshToPem };