cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
3959
Views
20
Helpful
5
Replies

Unable to install PEM/pkcs12 created by gnutls to ASA

bwkingston
Level 1
Level 1

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

1 Accepted Solution

Accepted Solutions

ahonore
Cisco Employee
Cisco Employee

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 ?

View solution in original post

5 Replies 5

ahonore
Cisco Employee
Cisco Employee

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 ?

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.

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.

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).

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