Skip to main content
3 of 8
added 2546 characters in body
StackzOfZtuff
  • 18.2k
  • 1
  • 54
  • 86

I'm not sure why you want to cut off bytes.

But here's what I found out from reading the RFCs/experimenting.

TLDR: The leading 0x00 is a SIGN BYTE that openssl adds for the printout only. It is NOT encoded in the certificate itself. And I guess you can cut of the leading byte (one of 0x02, 0x03 or 0x04) of the pubkey because it just stores info on whether compressed or noncompressed encoding is used.

#Sample key with leading nullbyte Here's an example of a random key I generated:

$ cat leading-nullbyte.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIN/37NFyCvL7brp4zljP83sNj1PvtFsp8dMR86EDwLZUoAoGCCqGSM49
AwEHoUQDQgAEK0acP7Ml6fgKy35YE7JGVP7AmNy7oJ6gl4QIqiwiSExbr4iDPfxT
81550HxXoiQiBJXBJxhgXYpIcJVmFGk20w==
-----END EC PRIVATE KEY-----

##ec printout leading-nullbyte.pem This key is shown with the "00:" prefix for the private key.

At least when using the openssl pretty printer...

$ openssl ec -noout -text -in leading-nullbyte.pem
read EC key
Private-Key: (256 bit)
priv:
    00:df:f7:ec:d1:72:0a:f2:fb:6e:ba:78:ce:58:cf:
    f3:7b:0d:8f:53:ef:b4:5b:29:f1:d3:11:f3:a1:03:
    c0:b6:54
pub:
    04:2b:46:9c:3f:b3:25:e9:f8:0a:cb:7e:58:13:b2:
    46:54:fe:c0:98:dc:bb:a0:9e:a0:97:84:08:aa:2c:
    22:48:4c:5b:af:88:83:3d:fc:53:f3:5e:79:d0:7c:
    57:a2:24:22:04:95:c1:27:18:60:5d:8a:48:70:95:
    66:14:69:36:d3
ASN1 OID: prime256v1
NIST CURVE: P-256

##asn1parse leading-nullbyte.pem ...however when actually looking inside the ASN1 encoding there is no 00: prefix. It just starts with df: right away. And also: length is given as 32 (l= 32). Not 33.

    5:d=1  hl=2 l=  32 prim:  OCTET STRING
      0000 - df f7 ec d1 72 0a f2 fb-6e ba 78 ce 58 cf f3 7b   ....r...n.x.X..{

Here's those lines in context:

$ openssl asn1parse -i -dump -in leading-nullbyte.pem
    0:d=0  hl=2 l= 119 cons: SEQUENCE
    2:d=1  hl=2 l=   1 prim:  INTEGER           :01
    5:d=1  hl=2 l=  32 prim:  OCTET STRING
      0000 - df f7 ec d1 72 0a f2 fb-6e ba 78 ce 58 cf f3 7b   ....r...n.x.X..{
      0010 - 0d 8f 53 ef b4 5b 29 f1-d3 11 f3 a1 03 c0 b6 54   ..S..[)........T
   39:d=1  hl=2 l=  10 cons:  cont [ 0 ]
   41:d=2  hl=2 l=   8 prim:   OBJECT            :prime256v1
   51:d=1  hl=2 l=  68 cons:  cont [ 1 ]
   53:d=2  hl=2 l=  66 prim:   BIT STRING
      0000 - 00 04 2b 46 9c 3f b3 25-e9 f8 0a cb 7e 58 13 b2   ..+F.?.%....~X..
      0010 - 46 54 fe c0 98 dc bb a0-9e a0 97 84 08 aa 2c 22   FT............,"
      0020 - 48 4c 5b af 88 83 3d fc-53 f3 5e 79 d0 7c 57 a2   HL[...=.S.^y.|W.
      0030 - 24 22 04 95 c1 27 18 60-5d 8a 48 70 95 66 14 69   $"...'.`].Hp.f.i
      0040 - 36 d3                                             6.

So the leading 0x00 is not actually encoded in the certificate file. While I have not taken a look at the openssl source code in regards to that I'm leaning towards calling this a printout bug.

According to RFC5915 (I think) an EC key is an UNSIGNED integer.

#Sample key WITHOUT leading nullbyte Here's another random key I generated. It does NOT have the 0x00 prefix when using the openssl ec pretty printer.

So this tells me that that 0x00 prefix is not necessarily there for each EC privkey.

$ cat no-leading-nullbyte.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAGcFoPfqfFZ5TDv71ZBBCctapiVKwZikE8HfKf61V1DoAoGCCqGSM49
AwEHoUQDQgAENPjKv9vobJnz1FSlSu5cNPyPwCvcsMmIr5HH92C+mZdVtanHwlTm
29IwOE5lSE8KMywIJs8pLCvX79kJhZ/upg==
-----END EC PRIVATE KEY-----

##ec printout no-leading-nullbyte.pem $ openssl ec -noout -text -in no-leading-nullbyte.pem read EC key Private-Key: (256 bit) priv: 01:9c:16:83:df:a9:f1:59:e5:30:ef:ef:56:41:04: 27:2d:6a:98:95:2b:06:62:90:4f:07:7c:a7:fa:d5: 5d:43 pub: 04:34:f8:ca:bf:db:e8:6c:99:f3:d4:54:a5:4a:ee: 5c:34:fc:8f:c0:2b:dc:b0:c9:88:af:91:c7:f7:60: be:99:97:55:b5:a9:c7:c2:54:e6:db:d2:30:38:4e: 65:48:4f:0a:33:2c:08:26:cf:29:2c:2b:d7:ef:d9: 09:85:9f:ee:a6 ASN1 OID: prime256v1 NIST CURVE: P-256

#asn1parse no-leading-nullbyte.pem Here again length is given as 32:

    5:d=1  hl=2 l=  32 prim:  OCTET STRING
      0000 - 01 9c 16 83 df a9 f1 59-e5 30 ef ef 56 41 04 27   .......Y.0..VA.'

Full parse:

$ openssl asn1parse -i -dump -in no-leading-nullbyte.pem
    0:d=0  hl=2 l= 119 cons: SEQUENCE
    2:d=1  hl=2 l=   1 prim:  INTEGER           :01
    5:d=1  hl=2 l=  32 prim:  OCTET STRING
      0000 - 01 9c 16 83 df a9 f1 59-e5 30 ef ef 56 41 04 27   .......Y.0..VA.'
      0010 - 2d 6a 98 95 2b 06 62 90-4f 07 7c a7 fa d5 5d 43   -j..+.b.O.|...]C
   39:d=1  hl=2 l=  10 cons:  cont [ 0 ]
   41:d=2  hl=2 l=   8 prim:   OBJECT            :prime256v1
   51:d=1  hl=2 l=  68 cons:  cont [ 1 ]
   53:d=2  hl=2 l=  66 prim:   BIT STRING
      0000 - 00 04 34 f8 ca bf db e8-6c 99 f3 d4 54 a5 4a ee   ..4.....l...T.J.
      0010 - 5c 34 fc 8f c0 2b dc b0-c9 88 af 91 c7 f7 60 be   \4...+........`.
      0020 - 99 97 55 b5 a9 c7 c2 54-e6 db d2 30 38 4e 65 48   ..U....T...08NeH
      0030 - 4f 0a 33 2c 08 26 cf 29-2c 2b d7 ef d9 09 85 9f   O.3,.&.),+......
      0040 - ee a6                                             ..

#Extra: How do I know that the 0x00 is a sign byte? ... by experimentation:

I generated a thousand keys like so:

$ for i in $(seq -w 1000); do echo $i; openssl ecparam -name prime256v1 -genkey -noout > key.$i.pem; done

And then I sorted and counted them by first byte like so:

$ for i in $(seq -w 1000); do openssl ec -noout -text -in key.$i.pem 2>/dev/null | grep '^priv:' -A1 | tail -1; done | sed 's/ *//' | sed 's/\(..\).*/\1/' | sort | uniq -c

    496 00
      3 01
      3 02
      4 03
      5 04
      4 05
      5 06
      3 07
      1 08
      3 09
      1 0a
      3 0b
      2 0c
      2 0d
      3 0e
      4 0f
      4 11
      6 12
      1 14
      7 15
      3 16
      7 17
      4 18
      1 19
      6 1a
      3 1b
      3 1c
      3 1d
     11 1e
      3 1f
      6 20
      4 21
      2 22
      3 23
      8 24
      3 25
      2 26
      7 27
      3 28
      7 29
      4 2a
      4 2b
      9 2c
      2 2d
      5 2e
      2 2f
      5 30
      7 31
      6 32
      3 33
      6 34
      8 35
      5 36
      2 37
      2 38
      5 39
      2 3a
      1 3b
      4 3c
      3 3d
      2 3e
      2 3f
      2 40
      3 41
      5 42
      5 43
      3 44
      4 45
      5 46
      4 47
      5 48
      5 49
      5 4a
      5 4b
      8 4c
      6 4d
      3 4e
      5 4f
      4 50
      2 51
      4 52
      2 53
      2 54
      3 55
      7 56
      2 57
      5 58
      3 59
      3 5a
      5 5b
      2 5d
      1 5e
      9 5f
      3 60
      3 61
      3 62
      6 63
      3 64
      1 65
      7 66
      4 67
      3 68
      4 69
      4 6a
      3 6b
      6 6c
      4 6d
      3 6e
      3 6f
      7 70
      4 71
      2 72
      5 73
      2 74
      1 75
      5 76
      3 77
     10 78
      2 79
      7 7a
      5 7b
      6 7c
      6 7d
      5 7e
      5 7f

The highest byte is 0x7f. And that is the final byte that still has a 0 as the most significant bit.

StackzOfZtuff
  • 18.2k
  • 1
  • 54
  • 86