Adding rsa key in device variables

I need to add rsa key as a device variable on balena cloud. However, newline characters are escaped and as a result, my app can’t authenticate with the key. How to maintain newline characters in the device variable. I used \n also in place of newline character but it doesn’t generate a newline.

This is a known issue and the current workaround is to base64 encode your variable. Please see: Problems with multiline environment variable - #33 by shaunmulligan for details.

I will add your request to our tracker to make sure your use case is noted. To help me understand better, can you explain why you need a newline in your RSA key? As far as I know, an RSA key doesn’t typically have a newline in it.

1 Like

It’s a PEM encoded RSA key with these lines at the start & the end “-----BEGIN RSA PRIVATE KEY-----” and “-----END RSA PRIVATE KEY-----” and key contents itself have newline characters. Without using newline characters, Openssl throws error of no start line in PEM routines while decoding.

There’s nothing that says you have to include the start and end lines in the variable tho… they’re not specifically required by the OpenSSL spec.

If it were me, even if my libraries did require the start and stop lines, I’d just add them programmatically and only include the “guts” of my keyfile in the variable.

Also you can safely remove the newlines from the key data itself. They’re not required by spec either. If your library won’t let you use a single-line string, you can always replace each newline in the variable with a ~ before setting the variable, and then re-replace the ~ with newlines in code after reading the variable and before sending it to OpenSSL.

I have tried not including the initial and the final lines but the error persists


I also used base64 method but it gives incorrect padding error

. I tried to fix it with adjusting the length of key as a multiple of 4 but then it throws the previous error again.

FWIW, I was able to reproduce the issue with supplying the key as a single line:

>>> from cryptography.hazmat.primitives.serialization import load_pem_private_key
>>> 
>>> # Example key from https://phpseclib.com/docs/rsa-keys converted to single line
>>> key="MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8QuKUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEmo3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2kTQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp79mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uyv/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00"
>>> load_pem_private_key(str.encode(key), password=None)
<snip Traceback>
ValueError: ('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [_OpenSSLErrorWithText(code=503841036, lib=60, reason=524556, reason_text=b'error:1E08010C:DECODER routines::unsupported')])

Where as the original multi-line key works:

>>> key='''-----BEGIN RSA PRIVATE KEY-----
... MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu
... KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm
... o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k
... TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7
... 9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy
... v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs
... /5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00
... -----END RSA PRIVATE KEY-----'''
>>> load_pem_private_key(str.encode(key), password=None)
<cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey object at 0x108a9d510>

So I think you’re quite right - the library will not accept the key without (at least some of) the new lines.

But simply wrapping it in the BEGIN/END markers seems to be enough, somewhat surprisingly:

>>> key="MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8QuKUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEmo3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2kTQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp79mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uyv/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00"
>>> pem_key="-----BEGIN RSA PRIVATE KEY-----\n"+key+"\n-----END RSA PRIVATE KEY-----"
>>> load_pem_private_key(str.encode(pem_key), password=None)
<cryptography.hazmat.backends.openssl.rsa._RSAPrivateKey object at 0x108a9d570>

May I suggest you set your environment variable to the key value, all on one line, and then put the BEGIN/END markers around it in your application code, like I’ve done above with pem_key?

1 Like

Thanks, this method worked out! The key is set as an ENV variable and newline characters are automatically changed to spaces on the dashboard. At the code level, spaces in the key are replaced with \n and subsequently wrapped with the markers.

Hello , @Perzade , @hraftery
How to fix this error ?

Just in case, in order to use a SSH private key in a device variable, I have to take BEGIN/END markers over, change spaces to Returns and re-put markers.

Here is what I’ve done :
In entrypoint.sh :

tmp_text=$(echo "$SSH_PRIVATE_KEY" | sed -e 's/-----BEGIN RSA PRIVATE KEY-----/<BEGIN_MARKER>/' \
                                -e 's/-----END RSA PRIVATE KEY-----/<END_MARKER>/')
modified_text=$(echo "$tmp_text" | sed -e 's/ /\'$'\n/g')
echo "$modified_text" | sed -e 's/<BEGIN_MARKER>/-----BEGIN RSA PRIVATE KEY-----/' \
                           -e 's/<END_MARKER>/-----END RSA PRIVATE KEY-----/' > /root/.ssh/id_rsa
chmod 600 /root/.ssh/id_rsa

In Dockerfile :

RUN mkdir -p /root/.ssh
ENV SSH_PRIVATE_KEY=""
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]

And now it’s working.

1 Like