Giter Club home page Giter Club logo

Comments (20)

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024 2

Hey, we are all learners... and I actually enjoy fiddling around with PKI and generating ASN.1 plus I find your git based avatar project interesting - so if I can help all the better.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024 1

Hey Kyle,

Thanks for reaching out, I am a big fan of YDKJS.

There is an accompanying blog post at

that points to this article on loading and decoding openSSH public key format:

which in turns point to the RFC describing the format in details

They use python, but the format descriptions was enough for me to get started.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024 1

From what I understand, you can always derive the public key from the private key, so if you are going to ask for the private key, no need to ask for the public key.

On the other hand the private key should never leave the owner's computer and travel over the network, it needs to stay private, or it will loose the properties we want for authentication.

Converting between formats should not require any crypto package, because it's only bit manipulation and reorder, there should be no cryptographic algorithms involved beside ASN.1 encode/decode and base64 or base64url encode/decode

I'll have a look at how to encode for PKCS8, but my guess is that it will involve the private key.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024

updating the code style to ES6 I found out that there is a method called decodePublicKey in ssh-util.js which reads an open ssh public key to its core elements which should allow to write a PEM encoded file. And there is no WebCryptoApi calls.

from js-keygen.

getify avatar getify commented on May 23, 2024

I saw that as well, but I couldn't figure out what to do with those raw elements to get it back into PEM form?

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024

I have an example live at https://js-keygen.surge.sh/convert.html is that what you are after?

from js-keygen.

getify avatar getify commented on May 23, 2024

This is great, thanks so much! Super helpful! :)

from js-keygen.

getify avatar getify commented on May 23, 2024

Sorry to be a pest... but I've tried the code you presented, and I found a few issues.

  1. I think this function is incorrect:

    function pemPublicKey(key) {
       return `---- BEGIN RSA PUBLIC KEY ----\n${wrap(key, 65)}---- END RSA PUBLIC KEY ----`;
    }

    I believe the PKCS1 PEM Public header format is -----BEGIN RSA PUBLIC----- (note the 5 -'s and no space). Also, it should wrap at the 64th column, not 65th. So I believe the function should be:

    function pemPublicKey(key) {
       return `-----BEGIN RSA PUBLIC KEY-----\n${wrap(key, 64)}-----END RSA PUBLIC KEY-----`;
    }

    When I make those changes, your code produces an identical file to the ssh-keygen CLI command. So that's good! :)

  2. Even though I'm able create this PKCS1 PEM format for the public key, it does not seem that I can get the webcrypto API to read that format. :(

    At least, I can't figure out what parameters to use with importKey(..) to make it work. This fails:

    function stripPemFormatting(str) {
       return str
       .replace(/^-----BEGIN (?:RSA )?(?:PRIVATE|PUBLIC) KEY-----$/m,"")
       .replace(/^-----END (?:RSA )?(?:PRIVATE|PUBLIC) KEY-----$/m,"")
       .replace(/[\n\r]/g,"");
    }
    
    function fromPem(keydataB64Pem) {
       var keydataB64 = stripPemFormatting(keydataB64Pem);
       var keydataS = window.atob(keydataB64);
       var keydata = Uint8Array.from(keydataS,x=>x.charCodeAt(0));
       return keydata;
    }
    
    var pkcs1PEM = publicSshToPem(`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKqc0Nv7zDbtouPytsDca2sxGRwcVl4TVr9beLiOOP3YrIcQww+hQSuc4tpVyE7WD9nRMbfwGMmWVxd0e88G1uFmaMulctvOpm6E1F2uOLhVT4CwHJhAfX7q8MFEiQ2+kGA/SsAjIVRydqlGrgTjaa0uolQVeXIZIGaCR4Si5hNwGU7ihqwfXc0fiF8tLpno2hcJpnAPAuo82NlVStmWhzDCgCzULWskStYP1/tCwVlr3fNXXNMoUWw1BJ5jG8V/1wvTMrnvPU4k+Yx87IJlr3lNY1FNVJTfsZZ57DUh8xGjCU3V2OoSk4W8g8P/o/3n3emS9nrWgRYIxwrD/2cbRbcOma70lQOWMSWcFQBWIEbb3+Dp1GwYoVg1BEJcV35s2yNCi8SuWoh+bjfK8ZrqlrRyvMeCV84Pq0W8ZjqnNUuFfvJt9ruw6410tVZW+hCZKPaxKTryNNdL2brM3b1IIl30mnZ2i7vCIbm865T/L3Qf7yCLYDA7ut7i7WaPgvuCu8MKZYnQVo4w9lLpND/1ygdEf/kAFEAQlAeNzRngNpXxlWixyUkDRT/4a2Yq1zTSy5YrRp7sO683/L1xKESiutlQ10zraPp2eY/UnRFV1bxRnxFeB2WUTYCLXOC7JZWd3MBVURk7Oedc5HUPx9Y4PkHMuSbVtTjyuRYTQNRZ/w8Q== [email protected]`);
    
    var keyTextBuffer = fromPem(pkcs1PEM);
    
    crypto.subtle.importKey(
      "spki",
       keyTextBuffer,
       {
          name: "RSASSA-PKCS1-v1_5",
          hash: { name: "SHA-256" },
       },
       true,
      ["verify"]
    );

All I get there is a DOMException with no other details. My guess is, either the webcrypto API can't read PKCS1 PEM's... -or- I'm missing some proper params in the algorithm object. Any thoughts?

Alternatively, if it's possible, maybe it'd be better to convert straight to the pkcs8 (or "spki", maybe?) public key format, since webcrypto seems not to support pkcs1 for either public or private keys.

from js-keygen.

getify avatar getify commented on May 23, 2024

Update: I found this: https://github.com/dominictarr/ssh-key-to-pem/blob/master/index.js

That converts from openssh key format straight to pkcs8. However, it's for node and uses several node packages that I'm not sure whether this code can be adapted for pure browser use or not. But it at least shows it's possible to go to the format we know webcrypto can read.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024
  1. Header and footer are very often discarded as separator by tooling. I now that openssl does read those headers, but the number of dash is irrelevant and the line length optional (it was meant to be send by email or seen on 80x24 terminals)
  2. Ah! if I had known you wanted to use the format to import into webcrypto... and use pkcs8 that would be a different story. The target format I used is pkcs1 :)

AFAIK pkcs8 is for private key, not public key... are you sure you want to convert the public key to pkcs8? that doesn't seem to fit what I understand.

from js-keygen.

getify avatar getify commented on May 23, 2024

Sorry for being so ignorant on this stuff. I'm stumbling around and don't even fully know what I'm doing. Appreciate your assistance!

from js-keygen.

getify avatar getify commented on May 23, 2024

My ultimate goal is to let a user pick, in their browser, a public key (in ssh, pkcs1, or pkcs8 format) and a private key (in pkcs1 or pkcs8 format), and basically normalize those both to pkcs8 if not already.

Since I know pkcs8 works in webcrypto, jsrsasign (my chosen fallback library for browsers without webcrypto), and node (node-rsa module), it seems like the best universal normalized text format to interchange keys between browser and server.

Additionally, if the user doesn't already have such keys, I can generate a pair for them, again in pkcs8 format.

My app lets someone pick their public key/private key from files and reads them in JS (or generates them!), creates a signature with private key and then verifies it with the public key, then transmits the public key and signature to server (ultimately forgetting any knowledge of their private key). I only verify the first time that they can correctly create a signature that verifies, so we know that will continue to be the case as long as they keep their private key. :)

I'm trying to make it so users don't have to think about what format of keys they have (since it's so confusing), so that's why it's attractive to just normalize everything to one text format that works everywhere I need it to.


BTW, I have a remaining issue to solve, even after this one, to get to that grand ideal scenario. jsrsasign can read pkcs1 private keys, but webcrypto cannot (I don't think). So I have to figure out how to convert pkcs1 private to pkcs8 private. This is trivial on the server with node-rsa, but I need to be able to do it in the browser.

Worst case: in that specific scenario, I have to use jsrsasign alongside webcrypto, instead of only using one or the other. Hope I don't have to do that, but I'm still just stumbling along for now.

from js-keygen.

getify avatar getify commented on May 23, 2024

If I can figure out a way to package/port rsa-keys lib + ssh-key-to-pem lib to both work in the browser, I think that combination would be the "universal" browser-side RSA key converter I'm referring to. Feels tantalizingly close. :)

from js-keygen.

getify avatar getify commented on May 23, 2024

you can always derive the public key from the private key

This is a great point... I knew that from a technical perspective, but hadn't considered it from the UX perspective. I was too fixated on the incremental tech side. It's probably much better to just ask them for only the private key. And the plus side is that it eliminates all the frustrations around converting between the various public key formats.

That still leaves the problem that I need to be able to import PKCS1 private keys, which jsrsasign understands but webcrypto doesn't. I would still need a way to convert a PKCS1 private key to a PKCS8 form.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024

So reading up on PKCS8, there are two contents available: PrivateKeyInfo or EncryptedPrivateKeyInfo, the last one will require a crypto library, but the first one doesn't.

But if the use case is about importing PKCS1 keys to webcrypto, we can convert to the jwk format instead, and use webcrypto to export to PKCS8 if required.

Would that suit?

I am still dubious about having people upload their private key.

from js-keygen.

getify avatar getify commented on May 23, 2024

PrivateKeyInfo or EncryptedPrivateKeyInfo

I'm not supporting encrypted private keys. If they have an encrypted private key, they'll have to decrypt it locally themselves, and paste in the unencrypted contents into the page.

But if the use case is about importing PKCS1 keys to webcrypto, we can convert to the jwk format instead, and use webcrypto to export to PKCS8 if required.

Specifically, the use-case is importing PKCS1 keys into the browser (only) to use, but then used either with webcrypto or with jsrsasign (if the browser doesn't have webcrypto). As such, that means the conversion itself ideally can't rely only on webcrypto or jsrsasign; conversion should work in either type of browser. That's why I've shyed away from jwk.

I am still dubious about having people upload their private key.

They don't upload their private key, they only read it into the page, which only uses the private key to create a digital signature. Only the public key and signature are uploaded. Private key is thrown away.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024

looking at the RFCs it looks fairly easy to wrap a PKCS1 into a PKCS8 because PKCS8 can be used as a container for PKCS1. That makes me think that if webcrypto can import PKCS8 it should be able to import PKCS1 as well. I'll have a go at it.

from js-keygen.

PatrickRoumanoff avatar PatrickRoumanoff commented on May 23, 2024

Have a look at https://js-keygen.surge.sh/1to8.html I did the simplest and prefixed the pkcs1 with a pkcs8 header. I might need to adjust the length of the pkcs1 entry, but openssl is happy with it...

from js-keygen.

getify avatar getify commented on May 23, 2024

Haven't had a chance to look at that yet.

But I've run into another roadblock. :( Apparently the webcrypto API does NOT allow you to extract/derive a public key from the private key. Awesome.

So, now I'm back to the drawing board, or... I have to use another crypto lib. Dammit why do they make this stuff so hard?

from js-keygen.

getify avatar getify commented on May 23, 2024

Actually, good news, I found a hack around this public-from-private limitation, in both jsrsasign and webcrypto. And the fact that this hack works makes me confused and frustrated that these APIs would have gone out of their way to not support this directly.

For webcrypto, the trick is to export the private key to jwk, then copy over only the relevant public key stuff (n and e, etc) into a new jwk-looking object, and import that back in. Just have to change the key_ops from "sign" to "verify". BAM, works. Why on earth would webcrypto have specifically disabled that direct use case but then let you do it so "easily" with such a hack? SMH.

For jsrsasign, the hack is even crazier. Just take the private key object, set isPrivate to false and isPublic to true, and pass it back into KEYUTIL.getPEM(..). BAM, public key. SMH x 2.

It's astonishing to me that neither API supports this what would seem like an incredibly common use case, but that this hack works so easily. Anyway... that problem solved.


I'll look soon at your PKCS1 to PKCS8 conversion and see if these APIs accept it.

from js-keygen.

Related Issues (4)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.