11-18-2012 08:35 PM
I've been pulling some hair out trying to figure out why cisco devices don't like my certificates. My primary need is to get a trustpoint set up with CA,cert,key on the ASA for VPN systems, however I'm having the same issues on my IOS devices. I created a pkcs12 with openssl a few months ago that imported with no issues, but now that I'm getting ready to move this lab to production I'm using gnutls certtool as I found it adds alt_dns and ip_address fields properly to the certificate, (which cost me a few more hairs trying to get to work with openssl's ca tool)
I'm including the current test certs below, don't worry I'm not using these in production
The maddening thing is that after I thought gnutls was generating certs incorrectly, I tried making a pkcs12 for a printserver and it imported with no issues.
Here's my command flow for creating these certs:
certtool --generate-privkey --disable-quick-random --outfile nn-ca.key
certtool --generate-self-signed --load-privkey nn-ca.key --outfile nn-ca.crt
certtool --generate-privkey --disable-quick-random --outfile nn-g0.key
certtool --generate-certificate --load-privkey nn-g0.key --outfile nn-g0.crt --load-ca-privkey nn-ca.key --load-ca-certificate nn-ca.crt
openssl pkcs12 -export -certfile nn-ca.crt -in nn-g0.crt -inkey nn-g0.key -out nn-g0.p12
openssl enc -base64 -in nn-g0.p12 -out nn-g0.base64.p12
The password for the attatched pkcs12 is "ciscohelp" without quotes. Thanks for any help
Solved! Go to Solution.
11-19-2012 01:55 AM
IOS also gives an encoding error when importing the PKCS#12:
CRYPTO_PKI: status = 0x701(E_BER_ENCODING : invalid encoding format for input data): Imported PKCS12 file failure
however the PKCS#12 itself is fine; the problem is with the certificates in it. You can tell by trying to authenticate a trustpoint using the nn-ca.cert file: both IOS and ASA refuse the certificate.
Upon closer examination of the DER content in those two certs, it looks like the public key encoding is wrong:
$ openssl asn1parse -i -dump -in nn-g0.crt | grep -A 18 'rsaEncryption$'
299:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
310:d=3 hl=4 l= 270 prim: BIT STRING
0000 - 00 30 82 01 09 02 82 01-00 de b3 e1 1f 59 7a bd .0...........Yz.
...
0100 - 86 7e c1 bb 62 18 40 f0-8f 02 03 01 00 01 .~..b.@.......
The public key modulus and exponent are encoded as a nested DER object at offset 310:
$ openssl asn1parse -i -dump -in nn-g0.crt -strparse 310
0:d=0 hl=4 l= 265 cons: SEQUENCE
4:d=1 hl=4 l= 256 prim: INTEGER :-214C1EE0A685422FC3F5...BF0F71
264:d=1 hl=2 l= 3 prim: INTEGER :010001
You can see that the modulus shows up as a negative number. This is not expected; the first bit in the bit string is always supposed to be zero, not one. If the first bit in the modulus is one, the value must be prepended with a leading 0 byte. Looking at the encoding at offset 310:
00 = no padding
30 82 01 09 = sequence of length 265
02 82 01 00 = integer of length 256
de b3 ... = modulus value with leading bit set to 1
I generated another set of certs using your CLI on my machine and got a correct result:
$ openssl asn1parse -i -dump -in gtls/nn-ca.crt | grep -A 19 'rsaEncryption$'
188:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
199:d=4 hl=2 l= 0 prim: NULL
201:d=3 hl=4 l= 271 prim: BIT STRING
0000 - 00 30 82 01 0a 02 82 01-01 00 d0 0c c4 2f 46 07 .0.........../F.
...
0100 - 64 60 1a ac b7 1f 53 ae-95 4d 02 03 01 00 01 d`....S..M.....
As you can see, the encoded value is : 00 d0 0c c4 ... which includes a leading zero byte to account for the fact that the modulus starts with D0. The nested decode shows a positive number:
$ openssl asn1parse -i -dump -in gtls/nn-ca.crt -strparse 201
0:d=0 hl=4 l= 266 cons: SEQUENCE
4:d=1 hl=4 l= 257 prim: INTEGER :D00CC42F46079BC7...71F53AE954D
265:d=1 hl=2 l= 3 prim: INTEGER :010001
and the certificate is imported correctly.
I could not reproduce this encoding problem with GnuTLS 2.12.14 on Ubuntu-latest. What version are you using ?
11-19-2012 01:55 AM
IOS also gives an encoding error when importing the PKCS#12:
CRYPTO_PKI: status = 0x701(E_BER_ENCODING : invalid encoding format for input data): Imported PKCS12 file failure
however the PKCS#12 itself is fine; the problem is with the certificates in it. You can tell by trying to authenticate a trustpoint using the nn-ca.cert file: both IOS and ASA refuse the certificate.
Upon closer examination of the DER content in those two certs, it looks like the public key encoding is wrong:
$ openssl asn1parse -i -dump -in nn-g0.crt | grep -A 18 'rsaEncryption$'
299:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
310:d=3 hl=4 l= 270 prim: BIT STRING
0000 - 00 30 82 01 09 02 82 01-00 de b3 e1 1f 59 7a bd .0...........Yz.
...
0100 - 86 7e c1 bb 62 18 40 f0-8f 02 03 01 00 01 .~..b.@.......
The public key modulus and exponent are encoded as a nested DER object at offset 310:
$ openssl asn1parse -i -dump -in nn-g0.crt -strparse 310
0:d=0 hl=4 l= 265 cons: SEQUENCE
4:d=1 hl=4 l= 256 prim: INTEGER :-214C1EE0A685422FC3F5...BF0F71
264:d=1 hl=2 l= 3 prim: INTEGER :010001
You can see that the modulus shows up as a negative number. This is not expected; the first bit in the bit string is always supposed to be zero, not one. If the first bit in the modulus is one, the value must be prepended with a leading 0 byte. Looking at the encoding at offset 310:
00 = no padding
30 82 01 09 = sequence of length 265
02 82 01 00 = integer of length 256
de b3 ... = modulus value with leading bit set to 1
I generated another set of certs using your CLI on my machine and got a correct result:
$ openssl asn1parse -i -dump -in gtls/nn-ca.crt | grep -A 19 'rsaEncryption$'
188:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
199:d=4 hl=2 l= 0 prim: NULL
201:d=3 hl=4 l= 271 prim: BIT STRING
0000 - 00 30 82 01 0a 02 82 01-01 00 d0 0c c4 2f 46 07 .0.........../F.
...
0100 - 64 60 1a ac b7 1f 53 ae-95 4d 02 03 01 00 01 d`....S..M.....
As you can see, the encoded value is : 00 d0 0c c4 ... which includes a leading zero byte to account for the fact that the modulus starts with D0. The nested decode shows a positive number:
$ openssl asn1parse -i -dump -in gtls/nn-ca.crt -strparse 201
0:d=0 hl=4 l= 266 cons: SEQUENCE
4:d=1 hl=4 l= 257 prim: INTEGER :D00CC42F46079BC7...71F53AE954D
265:d=1 hl=2 l= 3 prim: INTEGER :010001
and the certificate is imported correctly.
I could not reproduce this encoding problem with GnuTLS 2.12.14 on Ubuntu-latest. What version are you using ?
11-19-2012 02:26 AM
I'm using 2.8.5 from scientific linux (aka RHEL 6.3). I have Fedora 17 availible, I'm trying this now with 2.12.17.
Oh and thanks for making your first post just to help me!
Yep, it was just due to an old version of gnutls, the newer versions we're using work fine and it imported right away. I feel kind of numb for not trying this on my workstation before posting for help, but I really appriciate your very thoughal explination, I've learned more about openssl than I would have if I fixed it on my own.
Do you have any idea why 2.8.5 seems to be broken and if a command option can fix it? I've got to run a oscp/ca authority off of this scientific linux box and I'm hoping I can avoid installing a non repository version. Any how, just thinking out loud, you've helped me enough so I'll start looking for a fix myself.
11-19-2012 04:21 AM
You're welcome. The GnuTLS source code seems to indicate that the encoding of an RSA public key happens in _gnutls_x509_write_rsa_params() within lib/x509/mpi.c, and there is a slight difference between 2.8.5 and 2.10.0 (note: I picked a later version at random, not sure where the change was introduced); we have:
result = _gnutls_x509_write_int (spk, "modulus", params[0], 0);
...
result = _gnutls_x509_write_int (spk, "publicExponent", params[1], 0);
in 2.8.5, while the last parameter is set to 1 instead of 0 in 2.10.0.
Looking at the definition in lib/gnutls_mpi.c:
int _gnutls_x509_write_int (ASN1_TYPE node, const char *value, bigint_t mpi, int lz) {
...
if (lz)
result = _gnutls_mpi_print_lz (mpi, tmpstr, &s_len);
else
result = _gnutls_mpi_print (mpi, tmpstr, &s_len);
so I guess that the "lz" parameter indicates whether or not we should add a leading zero if needed. Digging further, the code ends up calling gcry_mpi_print() from the libgcrypt library with a parameter of GNUTLS_MPI_FORMAT_STD if "lz" is set to 1, and in the source for libgcrypt-latest, in mpi/mpicoder.c:
gcry_error_t
gcry_mpi_print (enum gcry_mpi_format format,
unsigned char *buffer, size_t buflen,
size_t *nwritten, struct gcry_mpi *a)
{
...
if (format == GCRYMPI_FMT_STD)
{
...
tmp = _gcry_mpi_get_buffer (a, &n, NULL);
...
if (n && (*tmp & 0x80))
{
n++;
extra=1;
}
which indeed looks at the highest order bit in the buffer. So, it seems that GnuTLS 2.8.5 does not call the function with the correct parameter set to add a leading zero when required, and there doesn't seem to be an option to change that.
11-19-2012 04:40 AM
That said, you may be able to generate the correct certs using OpenSSL only -- I use it for lab setups very frequently, and so far I've never run into any certificate attributes that could not be simulated using OpenSSL. You mentioned trouble with alt-dns and ip-address, if you give me a detailed example of what you require, I'll check if it can be done (those fields are not found in the ASA cert you posted).
11-19-2012 12:33 PM
I guess my biggest hangup with using only openssl was the fact that everywhere I looked for information where to put the alt_DNS and ip_Address information into openssl.cnf endedup encoding it in such a way that my browser didn't accept/understand those fields. It does work just fine on that printserver I mentioned, so even with gnutls's bug regular web servers work fine.
I'm starting to look into openssl's tools again because even thought the certs imported into my ASA just fine, it's still using a temp signed cert after making sure all of my configuration was fine (this isn't my first walk in that park at least). I tried to import a trustpoint into an IOS ap, and get a bad HMAC error, so even with the newer version of gnutls, cisco is still picky as it should be.
So if you can give me some pointers to include multiple IP addresses and DNS names into a openssl request, you would help me pull off something I've been putting off for about a year (I forgot to mention this is all a personal project lab and I'm just pushing on this because I want to get a website rolling)
I acutally figured it out, I don't know why the subjectAltName field wasn't working when I tried it a number of times before, but for anyone searching make sure you line looks like this:
subjectAltName="DNS:nn-g0,DNS:nn-g0.domain.net,DNS:vpn.domain.net,DNS:vpn.altdom.net,IP:10.0.2.1,IP:123.122.65.12"
Thanks again for all the help, I guess I just needed a fresh perspective
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide