Comprimento da chave privada da curva elíptica em Java
Criei um par de chaves EC usando a curva "secp256r1". É uma curva de 256 bits e a chave privada deve ser de 256 bits (32 bytes). Mas o que estou obtendo é uma chave privada de 39 bytes. Aqui está o meu código
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
Estou usando o JDK 1.8.0_144 e a biblioteca BouncyCastle. Aqui está 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>
Como obter 32 bytes de chave privada?
Respostas
A parte dependente do algoritmo de PKCS8, no elemento #2 da sequência, para EC é ela própria uma codificação DER, da estrutura SEC1 ECPrivateKey, também documentada em rfc5915 . Para SunEC, esta é uma SEQUÊNCIA de versão INTEGER e OCTETSTRING contendo os bytes de chave privada reais; os parâmetros context-0 opcionais e a chave pública context-1 são omitidos. Então você precisa analisar isso:
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);
Como você está usando o BouncyCastle, em vez de analisar manualmente o PKCS8, você pode usar a classe BouncyCastle para 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);
e, finalmente, em vez de passar pela codificação, você pode obter o valor da chave privada diretamente do ECPrivateKeyobjeto como um BigIntegervalor, que pode ser convertido em uma matriz de bytes, embora seja de comprimento variável em vez do comprimento fixo convencionalmente usado para chaves privadas EC, então pode ser necessário ajustá-lo:
byte[] bytes = ((ECPrivateKey)privateKey).getS().toByteArray();
System.out.println(bytes.length);
// may need left-trim or pad with zero(s)