简体   繁体   中英

PEM encoding of ECDSA in Java

I need help as to how to correctly construct the Java Private.pem. I have an ECDSA pem file created in java and one in python. I have correct implementation of the python one, but the java one isn't correct.

KeyPair pair = GenerateKeys();
PrivateKey priv = pair.getPrivate();
PublicKey pub = pair.getPublic();

// Convert to Bytes then Hex for new account params
byte[] bytePriv = priv.getEncoded();
byte[] bytePub = pub.getEncoded();

// save keys
SaveKeyToFile(bytePriv, "private", "private");

// Calls this function
public static void SaveKeyToFile(byte[] Key, String filename, String keyType) throws IOException, NoSuchAlgorithmException, NoSuchAlgorithmException, InvalidKeySpecException {
    StringWriter stringWriter = new StringWriter();
    PemWriter pemWriter = new PemWriter(stringWriter);
    PemObjectGenerator pemObject = new PemObject("EC " + keyType.toUpperCase() + " KEY", Key);


    pemWriter.flush();
    pemWriter.close();
    String privateKeyString = stringWriter.toString();
    FileUtils.writeStringToFile(new File(filename + ".pem"), privateKeyString);
}

For the ASN1 parse with OpenSSL of both Java and Python (Doesn't include hexdump because of its length):

JAVA
0:d=0  hl=3 l= 141 cons: SEQUENCE          
3:d=1  hl=2 l=   1 prim: INTEGER           :00
6:d=1  hl=2 l=  16 cons: SEQUENCE          
8:d=2  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
17:d=2  hl=2 l=   5 prim: OBJECT           :secp256k1
24:d=1  hl=2 l= 118 prim: OCTET STRING     [HEXDUMP] 

PYTHON
0:d=0  hl=2 l= 116 cons: SEQUENCE          
2:d=1  hl=2 l=   1 prim: INTEGER           :01
5:d=1  hl=2 l=  32 prim: OCTET STRING      [HEXDUMP] 
39:d=1  hl=2 l=   7 cons: cont [ 0 ]        
41:d=2  hl=2 l=   5 prim: OBJECT           :secp256k1
48:d=1  hl=2 l=  68 cons: cont [ 1 ]        
50:d=2  hl=2 l=  66 prim: BIT STRING    

As for the output PEM:

Java:
-----BEGIN EC PRIVATE KEY-----
MIGNAgEAMBAGByqGSM49AgEGBSuBBAAKBHYwdAIBAQQgiEc2TOzXj4mrUisuv+0p
xZ/z+Z/VyIvWug17zjNOPIKgBwYFK4EEAAqhRANCAATWi28vsiZdfqbqodDZc1uz
UFwNcu90l2ezayH0L4ZYB+MfReMCBFG/P6kn12GLj3DipqmvRucgmOlFVmggm+nH
-----END EC PRIVATE KEY-----

Python:
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEINTaEQvUvtOQlanp0lWv0KBcSs8IltKYH26OkoNxLQc5oAcGBSuBBAAK
oUQDQgAEcp3sseSpAXzIzWCwswYnsVnIZ0EEtkXl/CJWChQHilWLwWsxGq11/g/6
38UfZbsjE5TSf6zT8VXvTZE++u9c+A==
-----END EC PRIVATE KEY-----

Help would be very much appreciated!

(Sorry for the delay.)

Okay, you do need the algorithm-specific form but JCE PrivateKey.getEncoded() returns the PKCS8 (handles all algorithms) encoding, which is mostly the same Q as How can I convert a .p12 to a .pem containing an unencrypted PKCS#1 private key block? except you want ECC rather than RSA and (more important) you are using Java with BouncyCastle .

So, your options are:

  • write the PKCS8 encoding, either in PEM or directly in binary (DER), and use openssl ec or in 1.1.0 openssl pkey -traditional to convert it to the algorithm-specific PEM form, which is SEC1 (rfc5915 is basically a republication of SEC1).

  • write the PKCS8 encoding (either PEM or DER) and use openssl asn1parse -strparse to extract the algorithm-specific part, or determine the offset of the algorithm-specific part and extract it directly. A 256-bit ECC keypair DER encodes with mostly single-octet lengths, but the OID identifying the (named) curve may vary in size for different curves; as your example shows, it is 24+2 for secp256k1, so you can just give Arrays.copyOfRange(Key,26,Key.length) to your current logic.

    For ECC keypairs larger than 256-bits, the DER encoding may need to vary, and near the borderline could depend on whether the public value in the keypair is in uncompressed or compressed form, which you have no practical way to control (JCE doesn't provide for options within an encoding). This case would require handling at least some of the DER manually, in which case I would go to the next option instead.

  • BC, which you're already using, exposes the ability to manipulate PKCS8 structures and you can use that to extract the algorithm-specific SEC1 encoding, something like this:

import org.bouncycastle.asn1.ASN1Object;
    import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
    import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; // as defined by PKCS8
    ...
    PrivateKey k = /* whatever */;
    PemWriter w = new PemWriter (/* underlying writer */);
    PrivateKeyInfo i = PrivateKeyInfo.getInstance(ASN1Sequence.getInstance(k.getEncoded()));
    if( ! i.getPrivateKeyAlgorithm().getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey) ){
        throw new Exception ("not EC key");
    }
    ASN1Object o = (ASN1Object) i.parsePrivateKey(); 
    w.writeObject (new PemObject ("EC PRIVATE KEY", o.getEncoded("DER")));  
    // DER may already be the default but safer to (re)specify it 
    w.close();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM