Nexmo / nexmo-cli

Nexmo CLI (Command Line Interface)
https://nexmo.com
MIT License
78 stars 52 forks source link

Private key can't be used as-is when downloaded #81

Closed leggetter closed 7 years ago

leggetter commented 8 years ago

If you try to use the private key when it's downloaded, upon signing you are likely to get an error:

asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag

If you're seeing errors such as asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag then you should run the following on your key.txt:

openssl rsa -in key.txt -out key.txt

It may be that there are newline characters in the downloaded file that need to be stripped. Or some other set of characters. I've been informed that it's newline characters, but I haven't checked.

cbetta commented 8 years ago

Please can you list the actual problem? I don't think that proposed solution does what you think it does. As it stands that openssl command throws the same error as you described, wether there's a trailing space, no trailing space, wether I saved the file myself or let the nexmo CLi save the file, etc.

Additionally I am able to generate a public key off the private key using: ssh-keygen -y -e -f private.key suggesting the key is not in a bad format.

What command is failing for you?

cbetta commented 8 years ago

@leggetter any update on this?

leggetter commented 8 years ago

No. Will provide details when we get to a tutorial that requires a workflow that demonstrates it.

On Wed, 24 Aug 2016, 09:19 Cristiano Betta, notifications@github.com wrote:

@leggetter https://github.com/leggetter any update on this?

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/Nexmo/nexmo-cli/issues/81#issuecomment-241991187, or mute the thread https://github.com/notifications/unsubscribe-auth/AAUCr8KbaW4HMtr-vgry5MExf0g74La9ks5qi_6ggaJpZM4JTJ1q .

leggetter commented 8 years ago

Steps to reproduce:

1. Create an App and save the private key

nexmo app:create outbound-ex https://voice.ngrok.io/answer https://voice.ngrok.io/event --keyfile private.key

2. Attempt to create a JWT

Below APPLICATION_ID is the created application ID. PRIVATE_KEY_FILE is the name of the key file and is expected to be in the parent directory.

var fs = require('fs');
var uuid = require('node-uuid');
var jwt = require('jsonwebtoken');

function sign(cert, applicationId) {
  var toSign = {
    'iat': Date.now(),
        'application_id': applicationId,
         "jti": uuid.v1()
    };

  var token = jwt.sign(toSign, cert, {algorithm: 'RS256'});
  return token;
}

var key = fs.readFileSync(__dirname + '/../' + PRIVATE_KEY_FILE);
var token = sign(key, APPLICATION_ID);

When executing the above code you'll see the error similar to:

crypto.js:276
  var ret = this._handle.sign(toBuf(key), null, passphrase);
                         ^

Error: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
    at Error (native)
    at Sign.sign (crypto.js:276:26)
    at Object.sign (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jwa/index.js:54:45)
    at Object.jwsSign [as sign] (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jws/lib/sign-stream.js:23:24)
    at Object.module.exports [as sign] (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jsonwebtoken/sign.js:144:16)
    at sign (/Users/leggetter/nexmo/git/nexmo-node-quickstart/voice/outbound-call.js:20:19)
    at Object.<anonymous> (/Users/leggetter/nexmo/git/nexmo-node-quickstart/voice/outbound-call.js:25:13)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:458:32)

3. Fix the Key File

To fix this you need to modify the keyfile:

› openssl rsa -in private.key -out private.key
writing RSA key

4. Run the same code. No error.

cbetta commented 8 years ago

@leggetter is this signing process documented somewhere? Would love to reimplement in Ruby just to ensure it's not a Node issue.

Also: this signing doesn't actually make any API calls. After you fix the key and sign the token, does the API accept the signature?

leggetter commented 8 years ago

@cbetta There's a Ruby example on this page https://docs.nexmo.com/voice/voice-api#first_call

The API calls do succeed after fixing the key, yes.

It could be a Node thing. Although I think @sammachin suggested he'd seen a problem with Python too.

cbetta commented 8 years ago

@leggetter followed the Ruby example and signing just works...

Going to try the Node sample next to ensure that it fails here.

cbetta commented 8 years ago

Error confirmed. Interestingly the conversion you suggested throws the following error here:

unable to load Private Key
34783:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/crypto/asn1/tasn_dec.c:1344:
34783:error:0D06C03A:asn1 encoding routines:ASN1_D2I_EX_PRIMITIVE:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/crypto/asn1/tasn_dec.c:848:
34783:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/crypto/asn1/tasn_dec.c:768:Field=n, Type=RSA
34783:error:0D09A00D:asn1 encoding routines:d2i_PrivateKey:ASN1 lib:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/crypto/asn1/d2i_pr.c:99:
34783:error:0907B00D:PEM routines:PEM_READ_BIO_PRIVATEKEY:ASN1 lib:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/crypto/pem/pem_pkey.c:125:
leggetter commented 8 years ago

@cbetta that looks like it can't find the file.

cbetta commented 8 years ago

@leggetter it's most certainly there.

I noticed something else interesting:

I ran the app creation command without saving the key to file. I then copy pasted the key to a new file myself. I then re-ran the JS code and got the same error. This seems to suggest it's not an issue with the Node CLI but a deeper issue with either the key, or the JS code loading the key. Am I wrong?

leggetter commented 8 years ago

@leggetter it's most certainly there.

Are you sure? Kidding! 😄

What if you run the Ruby code on the copied key? I'm wondering if it is Node specific handling of some sort of encoding.

cbetta commented 8 years ago

@leggetter no issue in Ruby.

Could you do me a favor and try the same? Skip the CLI, make an API call using CURL or the Node lib, save the key to file yourself, and then run the Node command on it. If it fails at this point we need to probably look at the cert returned by the API, or at the correct way to load the key in Node.

I tried making it work in Python but I keep getting ImportError: No module named Crypto.Hash.SHA256 even though I have the crypto module installed.

cbetta commented 8 years ago

I managed to make the Python sample work (reinstall pycrypto which is not even listed as a dependency) and it works fine!

leggetter commented 8 years ago

1) Make a CURL request to create the app:

base_url='https://api.nexmo.com'
  version='/beta'
  action='/account/applications/?'
  key='API_KEY'
  secret='API_SECRET'
  name='MyFirstApplication'
  type='voice'
  answer_url='https://example.com/ncco'
  event_url='https://example.com/call_status'

  curl $base_url$version$action \
    -d api_key=$key \
    -d api_secret=$secret \
    -d name=$name \
    -d type=$type \
    -d answer_url=$answer_url \
    -d event_url=$event_url

2) Copy the output private key text and put into private.key

3) Run this example and get an expected error relating to the fact newline characters haven't been replaced (likely cause):

› node voice/outbound-call.js
crypto.js:282
  var ret = this._handle.sign(toBuf(key), null, passphrase);
                         ^

Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
    at Error (native)
    at Sign.sign (crypto.js:282:26)
    at Object.sign (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jwa/index.js:54:45)
    at Object.jwsSign [as sign] (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jws/lib/sign-stream.js:23:24)
    at Object.module.exports [as sign] (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jsonwebtoken/sign.js:144:16)
    at sign (/Users/leggetter/nexmo/git/nexmo-node-quickstart/voice/outbound-call.js:23:19)
    at Object.<anonymous> (/Users/leggetter/nexmo/git/nexmo-node-quickstart/voice/outbound-call.js:28:13)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
  1. Replace \n with real newline characters and run the same example:
› node voice/outbound-call.js
crypto.js:282
  var ret = this._handle.sign(toBuf(key), null, passphrase);
                         ^

Error: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
    at Error (native)
    at Sign.sign (crypto.js:282:26)
    at Object.sign (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jwa/index.js:54:45)
    at Object.jwsSign [as sign] (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jws/lib/sign-stream.js:23:24)
    at Object.module.exports [as sign] (/Users/leggetter/nexmo/git/nexmo-node-quickstart/node_modules/jsonwebtoken/sign.js:144:16)
    at sign (/Users/leggetter/nexmo/git/nexmo-node-quickstart/voice/outbound-call.js:23:19)
    at Object.<anonymous> (/Users/leggetter/nexmo/git/nexmo-node-quickstart/voice/outbound-call.js:28:13)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
  1. Fix up the private key which "works for me":
› openssl rsa -in private.key -out private.key
writing RSA key
  1. Run the example and it now works.

@cbetta could we add an outbound calling example to https://github.com/nexmo-community/nexmo-ruby-quickstart (since I think you have the basic code) and then I can try the same process there? I'm wondering if step 4 will be required for Ruby (or other languages) and that the ASN1_CHECK_TLEN:wrong tag is Node specific.

leggetter commented 8 years ago

Changing the header and footer in the original downloaded keyfile also fixes the problem:

Current header: BEGIN RSA PRIVATE KEY Current footer: END RSA PRIVATE KEY

Remove RSA:

Updated header: BEGIN PRIVATE KEY Updated footer: END PRIVATE KEY

My present suggest is that the Application API should be updated to return the keyfile without the RSA. With RSA is PKCS#1. Without is PKCS#8. I believe we intend to be using PKCS#8.

cbetta commented 8 years ago

Nice find! Seems indeed that the API should probably be updated.

leggetter commented 7 years ago

Fixed with an API update. The problem was https://github.com/Nexmo/nexmo-cli/issues/81#issuecomment-244353601