Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elliptic Curve Private Key Length in Java

I created a EC key pair using "secp256r1" curve. It is 256-bit curve and private key should be 256 bits (32 bytes). But what I'm getting is 39 bytes private key. Here is my code

 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); //Provider is SunEC version 1.8
 ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
 kpg.initialize(ecSpec, new SecureRandom());
 KeyPair ecKeyPair = kpg.generateKeyPair();
 PrivateKey privateKey = ecKeyPair.getPrivate();
 
 ASN1Sequence sequence = DERSequence.getInstance(privateKey.getEncoded());
 DEROctetString subjectPrivateKey =  (DEROctetString) sequence.getObjectAt(2);
 byte[] privateKeyBytes = subjectPrivateKey.getOctets();

 System.out.println("PrivateKeyBytes.length: " + privateKeyBytes.length); // Expected length is 32, but actual is 39 

I'm using JDK 1.8.0_144 and BouncyCastle library. Here is pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>pki</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-debug-jdk15on</artifactId>
            <version>1.65</version>
        </dependency>
    </dependencies>
</project> 

How to get 32 bytes of private key?

like image 670
karasekv Avatar asked Jun 14 '26 18:06

karasekv


1 Answers

The algorithm-dependent part of PKCS8, at element #2 of the sequence, for EC is itself a DER encoding, of the SEC1 structure ECPrivateKey, also documented at rfc5915. For SunEC this is a SEQUENCE of INTEGER version and OCTETSTRING containing the actual privatekey bytes; the optional context-0 parameters and context-1 publickey are omitted. So you need to parse that:

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC",args[0]);
    kpg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
    KeyPair ecKeyPair = kpg.generateKeyPair();
    PrivateKey privateKey = ecKeyPair.getPrivate();
    byte[] pkcs8 = privateKey.getEncoded();
    
    // slightly reorganized
    DEROctetString wrapped = (DEROctetString) DERSequence.getInstance(pkcs8).getObjectAt(2);
    System.out.println (wrapped.getOctets().length);
    // added
    DEROctetString raw = (DEROctetString) DERSequence.getInstance( wrapped.getOctets() ).getObjectAt(1);
    System.out.println (raw.getOctets().length);

Since you are using BouncyCastle, rather than manually parse the PKCS8 you can use the BouncyCastle class for org.bouncycastle.asn1.pkcs.PrivateKeyInfo:

    PrivateKeyInfo info = PrivateKeyInfo.getInstance (DERSequence.getInstance(pkcs8));
    DEROctetString raw2 = (DEROctetString)( DERSequence.getInstance(info.parsePrivateKey()) ).getObjectAt(1);
    System.out.println (raw2.getOctets().length);

and finally, rather than going through the encoding, you can get the privatekey value directly from the ECPrivateKey object as a BigInteger value, which can be converted to a byte array, although it is variable length instead of the fixed length conventionally used for EC privatekeys, so you may need to adjust it:

    byte[] bytes = ((ECPrivateKey)privateKey).getS().toByteArray();
    System.out.println(bytes.length);
    // may need left-trim or pad with zero(s)
like image 67
dave_thompson_085 Avatar answered Jun 17 '26 09:06

dave_thompson_085