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.