RSA signing verification between platforms

  1. 4 days ago

    Kem T

    Mar 24 Pre-Release Testers, Xojo Pro, XDC Speakers, MVP Connecticut

    I need to verify data that is RSA signed by a JavaScript app, but the Xojo Crypto library is older and limited in options. I control the JS code, so what is the best, most secure way to achieve this? (We use the crypto library on the JS side, but are open to using others.)

    The solution must work on the three desktop platforms, can use Shell calls, but cannot rely on plugins.

  2. Thom M

    Mar 24 Pre-Release Testers Greater Hartford Area, CT

    I have not filled in my "verify" version of this code yet, but this works for generating a signature compatible with Xojo's keys. The input for privateKey in this case is a Xojo private key converted into PEM format:

    Protected Function PEMEncodePrivateKey(Key As String) as String
      Var Base64 As String = EncodeBase64(Crypto.DEREncodePrivateKey(Key), 0)
      Var Lines() As String = Array("-----BEGIN PRIVATE KEY-----")
      While Base64.Length > 64
        Lines.AddRow(Base64.Left(64))
        Base64 = Base64.Middle(64)
      Wend
      If Base64.Length > 0 Then
        Lines.AddRow(Base64)
      End If
      Lines.AddRow("-----END PRIVATE KEY-----")
      Return Lines.Join(Encodings.UTF8.Chr(10))
    End Function
    async createSignature (privateKey, data) {
    	try {
    		if (!('TextEncoder' in window)) {
    			console.log('No TextEncoder');
    			return false;
    		}
    		let lines = privateKey.trim().split('\n');
    		if ((lines[0].indexOf('BEGIN PRIVATE KEY') === -1 || lines[lines.length - 1].indexOf('END PRIVATE KEY') === -1) && (lines[0].indexOf('BEGIN RSA PRIVATE KEY') === -1 || lines[lines.length - 1].indexOf('END RSA PRIVATE KEY') === -1)) {
    			console.log('Unable to find private key tags');
    			return false;
    		}
    		
    		lines = lines.slice(1, lines.length - 1);
    		let bytes = Buffer.from(lines.join(''), 'base64').buffer;
    		
    		let importedKey = await window.crypto.subtle.importKey('pkcs8', bytes, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-1' }, false, [ 'sign' ]);
    		let encoder = new TextEncoder();
    		let signedData = await window.crypto.subtle.sign('RSASSA-PKCS1-v1_5', importedKey, encoder.encode(data));
    		return String.fromCharCode.apply(null, new Uint8Array(signedData));
    	} catch (err) {
    		console.log('Unable to sign: ' + err.message);
    		return false;
    	}
    }

    Perhaps this serves as some inspiration?

  3. Kem T

    Mar 24 Pre-Release Testers, Xojo Pro, XDC Speakers, MVP Connecticut

    Without testing, is "window.crypto.subtle" the same as using the crypto lib in a node-js app? (What I know about JS could fill a thimble, frankly.)

  4. Thom M

    Mar 24 Pre-Release Testers Greater Hartford Area, CT

    I don't have an exact answer for you. I know I ran into problems following the NodeJS documentation because it is for the server-side code. Some of its functionality is ported to the native client-side crypto routines, but I couldn't tell you exactly which ones.

  5. Kem T

    Mar 24 Pre-Release Testers, Xojo Pro, XDC Speakers, MVP Connecticut

    I'll play, thanks.

  6. Kem T

    Mar 24 Pre-Release Testers, Xojo Pro, XDC Speakers, MVP Connecticut

    Does this do the same thing in Xojo, or have I missed something?

    Var Base64 As String = EncodeBase64(Crypto.DEREncodePrivateKey(Key), 64)
    Var Lines() As String = Array( _
      "-----BEGIN PRIVATE KEY-----", _
      Base64.ReplaceLineEndings( &uA ), _
      "-----END PRIVATE KEY-----" )
    
    Return String.FromArray( Lines, &uA )
  7. Thom M

    Mar 24 Pre-Release Testers Greater Hartford Area, CT

    I think at the time I wrote that, I was using a "new framework" compatible implementation that did not introduce line breaks. So yes, I believe your version is more sane. But you also don't need the array at all.

    Return "-----BEGIN PRIVATE KEY-----" + &uA + EncodeBase64(Crypto.DEREncodePrivateKey(Key), 64).ReplaceLineEndings(&uA) + &uA + "-----END PRIVATE KEY-----"
  8. Kem T

    Mar 24 Pre-Release Testers, Xojo Pro, XDC Speakers, MVP Connecticut

    I tried this.

    const privateKey = `-----BEGIN PRIVATE KEY-----
    MIIEoQIBAAKCAQEArHqAMTbEdCTPVb2hjJhaGfr7Ndbiz0+ydtK5E1EWpL5F9oC3
    2gvwirFkHucp6ghA9pDI2kAAabYNoS7GFcaVo9F40k3T3fL4vCwRTinWJIsbDnyo
    Q2RGaYe9elN0j2giRQtH8lquujH/p6SbfBr8f1sdEsHXzLxEmFGGV+gVRzi5Vhlx
    Y5U9jk1ZRpbB8DCGpDKKJB183np1wHORG9RFCEQ4H4QhjCz+1LOW3HFh3FyFkeQS
    XOOFhM03MptS+Iv7gk7aeG2guhyDKb8RZxf7BsFw4S4Bn7zhgC7eCY2o0JvqdZ8g
    wKNbCvL10ISe9E0ZD38253kaSczVSxxT+VdytQIBEQKCAQAAgd294svfNlHtbbv1
    HpDUZ+QgiOoUL6txSmhzQb6PixnORyexlR4Sen+Koc8uniRVDfqRNOTlNH0HQCM3
    yB0NSCU/3Zf23VOOGzxJQ+ND30eb5sRCdQA1BCn4E9Jp1WN9MPlvZSksN91dGtZK
    5/w5mR1zQVK0jhtOwP4i5NLi2EGRTpA/vVgeZULfF8FO5+HhOsUh82Y6MZa94sxj
    HTfBCDFgHMEi5aTF2aXPkPyuioB1D4vAtuEhX2oc5ye9rihiBZmb2Pr0Gv7p3fjP
    +8JKMq/tSB/Jmf1ZwvVNe2AfWQqNkdNpNwRCfu045emxpUPbh0+Jen1r18LhukeI
    sYvpAoGBANaNan4L/W1CVcFLCvQgIni68dayjRCEzGu3eKk0ohr+fc6V342okLRZ
    O/a9C3WTXosmiCa+wB5ZR+JOTI80faLxm9U3bgPVI6b+wiTs0VJho3GzfJ4FndSk
    MH2Oh98ON9dsbBMZ0a81b8ZzigYD+QaV/C1VCZReS5h22SGZTLeNAoGBAM3MV16w
    Hsftesev/xEEFg7HMuJaTKcP0dXXw3REZB2q5NXjdBGKnXO2Z2hy1lR5C17ytk+r
    QGbYzGaBTfvYwLZhQwJE0lCxHk2QLng/6J4ZzFgJ5yvZmpGlEJWTKiAgIg8DhkXp
    wAOE8B/LoDkA1fLpzWb7wPKw8mkI3DDUPOnJAoGAGT3QSxB4Kvi+y3I9hiHl8BX+
    VX5q8uJyZwaGqn6pqNKlY5kpW/W2q85DSjRbs3q/1CKmmyWAA5IIdPonH+gOx+Aw
    c2/u00ZAbf/amu6vNt5PdsnSbPGaGQRB8KdbR2sVoN+UPnuCFJzf+TrE8aYdTBGl
    MoJ5mPwI5MKwIhIJBokCgYEAqXsaxnLsLCz7s4HhHRJshKQLyXeKa3Zwkfz7ULDK
    60FxKJ0yaMyBqpY3CrjsvglUqIulMo0H3DoRvdPl3nZEWfW+tpMHjb8J5YXL6o77
    zX6oSICgQjq7hwBoArVt/FayovPX/VcWmXyJg5iiENODBEgSkQuP1uwS7RZa+wkj
    GuECgYAHQ2O1hL8aKxbZR/2OLNNhbbwJHxAlM2eXofavxx59eiA5byUbStV474qT
    G7J7dlVNKtv3E22HovdXH0jOXFfqO9IyOWkEVpBQTiPezpkdo5ZzODUrNSAK8ikA
    TNp1SmNgc9fQFUBMxA2lbhqWgLzE2lYsDek4prZwYmW4EhAxLw==
    -----END PRIVATE KEY-----
    `
    
    let lines = privateKey.trim().split('\n')
    if (
    	(lines[0].indexOf('BEGIN PRIVATE KEY') === -1 ||
    		lines[lines.length - 1].indexOf('END PRIVATE KEY') === -1) &&
    	(lines[0].indexOf('BEGIN RSA PRIVATE KEY') === -1 ||
    		lines[lines.length - 1].indexOf('END RSA PRIVATE KEY') === -1)
    ) {
    	console.log('Unable to find private key tags')
    	return false
    }
    
    lines = lines.slice(1, lines.length - 1)
    let bytes = Buffer.from(lines.join(''), 'base64').buffer
    
    const crypto = require('crypto')
    const sign = crypto.createSign('SHA1')
    sign.write('data')
    sign.end()
    
    const sig = sign.sign(privateKey)
    console.log(sig)

    (That's a test private key that won't be used for anything.)

    The error stemming from the "sig = sign( ..." line:

    internal/crypto/sig.js:105
      const ret = this[kHandle].sign(data, format, type, passphrase, rsaPadding,
                                ^
    
    Error: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
        at Sign.sign (internal/crypto/sig.js:105:29)
        at Object.<anonymous> (/Users/ktekinay/Documents/MacTechnologies/Clients/AMPS/Repos/dragon-js/src/try.js:49:18)
        at Module._compile (internal/modules/cjs/loader.js:1158:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
        at Module.load (internal/modules/cjs/loader.js:1002:32)
        at Function.Module._load (internal/modules/cjs/loader.js:901:14)
        at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
        at internal/main/run_main_module.js:18:47 {
      opensslErrorStack: [
        'error:0907B00D:PEM routines:PEM_read_bio_PrivateKey:ASN1 lib',
        'error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error',
        'error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error'
      ],
      library: 'asn1 encoding routines',
      function: 'asn1_check_tlen',
      reason: 'wrong tag',
      code: 'ERR_OSSL_ASN1_WRONG_TAG'
    }
  9. Kem T

    Mar 24 Pre-Release Testers, Xojo Pro, XDC Speakers, MVP Connecticut

    Still open to suggestions, but this has become academic.

  10. Thom M

    Mar 24 Pre-Release Testers Greater Hartford Area, CT

    Unfortunately I don't know. Even my own code I feel like I got lucky with.

  11. 19 hours ago

    Paul M

    19 hours ago Pre-Release Testers, Xojo Pro Eastern Massachusetts, USA

    @Kem T const privateKey =

    OpenSSL doesn't like that key as is, but likes it with these markers "-----BEGIN RSA PRIVATE KEY-----" "-----END RSA PRIVATE KEY-----", maybe the JS library has similar issues.

    (tested with openssl rsa -check -noout -in keyFile.pem)

or Sign Up to reply!