Generating RSA keys in PKCS#1 format in Java

前端 未结 6 639
有刺的猬
有刺的猬 2020-11-28 08:33

When I generate an RSA key pair using the Java API, the public key is encoded in the X.509 format and the private key is encoded in the PKCS#8 format. I\'m looking to encod

相关标签:
6条回答
  • 2020-11-28 08:41

    I know this is old post. but I spent two days to solve this problem and finally find BouncyCastle can do that

    ASN1Encodable

    http://www.bouncycastle.org/docs/docs1.5on/org/bouncycastle/asn1/ASN1Encodable.html

    0 讨论(0)
  • 2020-11-28 08:42

    I was trying to generate OpenSSL-friendly RSA public keys in DER format using BountyCastle J2ME library ported to BlackBerry, my code:

    public void testMe() throws Exception {
      RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
      generator.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001),
                     new SecureRandom(), 512, 80));
      AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();
    
      RSAKeyParameters params =  (RSAKeyParameters) keyPair.getPublic();
      RSAPublicKeyStructure struct = new RSAPublicKeyStructure(params.getModulus(), 
                                                               params.getExponent());
    
      SubjectPublicKeyInfo info = 
        new SubjectPublicKeyInfo(new AlgorithmIdentifier("1.2.840.113549.1.1.1"), 
                                 struct);
    
      byte[] bytes = info.getDEREncoded();
    
      FileOutputStream out = new FileOutputStream("/tmp/test.der");
    
      out.write(bytes);
      out.flush();
      out.close();
    }
    

    Key was still incorrect:

    $ openssl asn1parse -in test.der -inform DER -i
    0:d=0  hl=2 l=  90 cons: SEQUENCE          
    2:d=1  hl=2 l=  11 cons:  SEQUENCE          
    4:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
    15:d=1  hl=2 l=  75 prim:  BIT STRING     
    

    I changed org.bouncycastle.asn1.x509.AlgorithmIdentifier

    public AlgorithmIdentifier(
        String     objectId)
    {
        this.objectId = new DERObjectIdentifier(objectId);
        // This line has been added
        this.parametersDefined = true;
    }
    

    And now have nice key:

    $ openssl asn1parse -in test.der -inform DER -i
    0:d=0  hl=2 l=  92 cons: SEQUENCE          
    2:d=1  hl=2 l=  13 cons:  SEQUENCE          
    4:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
    15:d=2  hl=2 l=   0 prim:   NULL              
    17:d=1  hl=2 l=  75 prim:  BIT STRING 
    

    Which can be used to encrypt:

    $ echo "123" | openssl rsautl -pubin  -inkey test.der -encrypt -keyform DER -out y
    $ wc -c y
    64 y
    
    0 讨论(0)
  • 2020-11-28 08:47

    The BouncyCastle framework has a PKCS1 Encoder to solve this: http://www.bouncycastle.org/docs/docs1.6/index.html

    0 讨论(0)
  • 2020-11-28 08:56

    I wrote a C programme to convert pkcs8 private key to pkcs1. It works!

    /*****************************************
        convert pkcs8 private key file to pkcs1
    
        2013-1-25   Larry Wu     created
     ****************************************/
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    
    #include <openssl/rsa.h>
    #include <openssl/bio.h> 
    #include <openssl/err.h> 
    #include <openssl/pem.h>
    #include <openssl/engine.h>
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <time.h>
    #include <assert.h>
    #include <stdarg.h>
    
    #include <fstream>
    #include <sstream>
    #include <iostream>
    #include <map>
    #include <set>
    #include <list>
    #include <vector>
    
    using namespace std;
    
    
    #define MY_TRACE_ERROR printf
    
    
    /*
        gcc -Wall -o pkcs_8to1 pkcs_8to1.cpp -g -lstdc++ -lcrypto -lssl
    */
    int main(int argc, char **argv)
    {
        EVP_PKEY * pkey = NULL;
        string kin_fname;
        FILE *kin_file = NULL;
        string kout_fname;
        FILE *kout_file = NULL;
    
        // param
        if(argc != 3)
        {
            printf("Usage: %s <pkcs8_key_file> <pkcs1_key_file>\n", argv[0]);
            return 1;
        }
    
        kin_fname = argv[1];
        kout_fname = argv[2];
    
    
        // init
        OpenSSL_add_all_digests();
        ERR_load_crypto_strings();
    
        // read key
        if((kin_file = fopen(kin_fname.c_str(), "r")) == NULL)
        {
            MY_TRACE_ERROR("kin_fname open fail:%s\n", kin_fname.c_str());
            return 1;
        }
    
        if ((pkey = PEM_read_PrivateKey(kin_file, NULL, NULL, NULL)) == NULL) 
        {
            ERR_print_errors_fp(stderr);
            MY_TRACE_ERROR("PEM_read_PrivateKey fail\n");
            fclose(kin_file);
            return 2;
        }
    
        // write key
        if((kout_file = fopen(kout_fname.c_str(), "w")) == NULL)
        {
            MY_TRACE_ERROR("kout_fname open fail:%s\n", kout_fname.c_str());
            return 1;
        }
    
        if (!PEM_write_PrivateKey(kout_file, pkey, NULL, NULL, 0, NULL, NULL)) 
        {
            ERR_print_errors_fp(stderr);
            MY_TRACE_ERROR("PEM_read_PrivateKey fail\n");
            fclose(kout_file);
            return 2;
        }
    
        // clean
        fclose(kin_file);
        fclose(kout_file);
        EVP_PKEY_free(pkey);
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-28 08:58

    You will need BouncyCastle:

    import org.bouncycastle.asn1.ASN1Encodable;
    import org.bouncycastle.asn1.ASN1Primitive;
    import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
    import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
    import org.bouncycastle.util.io.pem.PemObject;
    import org.bouncycastle.util.io.pem.PemWriter;
    

    The code snippets below have been checked and found working with Bouncy Castle 1.52.

    Private key

    Convert private key from PKCS8 to PKCS1:

    PrivateKey priv = pair.getPrivate();
    byte[] privBytes = priv.getEncoded();
    
    PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
    ASN1Encodable encodable = pkInfo.parsePrivateKey();
    ASN1Primitive primitive = encodable.toASN1Primitive();
    byte[] privateKeyPKCS1 = primitive.getEncoded();
    

    Convert private key in PKCS1 to PEM:

    PemObject pemObject = new PemObject("RSA PRIVATE KEY", privateKeyPKCS1);
    StringWriter stringWriter = new StringWriter();
    PemWriter pemWriter = new PemWriter(stringWriter);
    pemWriter.writeObject(pemObject);
    pemWriter.close();
    String pemString = stringWriter.toString();
    

    Check with command line OpenSSL that the key format is as expected:

    openssl rsa -in rsa_private_key.pem -noout -text
    

    Public key

    Convert public key from X.509 SubjectPublicKeyInfo to PKCS1:

    PublicKey pub = pair.getPublic();
    byte[] pubBytes = pub.getEncoded();
    
    SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
    ASN1Primitive primitive = spkInfo.parsePublicKey();
    byte[] publicKeyPKCS1 = primitive.getEncoded();
    

    Convert public key in PKCS1 to PEM:

    PemObject pemObject = new PemObject("RSA PUBLIC KEY", publicKeyPKCS1);
    StringWriter stringWriter = new StringWriter();
    PemWriter pemWriter = new PemWriter(stringWriter);
    pemWriter.writeObject(pemObject);
    pemWriter.close();
    String pemString = stringWriter.toString();
    

    Check with command line OpenSSL that the key format is as expected:

    openssl rsa -in rsa_public_key.pem -RSAPublicKey_in -noout -text
    

    Thanks

    Many thanks to the authors of the following posts:

    • https://stackoverflow.com/a/8713518/1016580
    • https://stackoverflow.com/a/14052651/1016580
    • https://stackoverflow.com/a/14068057/1016580

    Those posts contained useful, though sometimes outdated info (i.e. for older versions of BouncyCastle), that helped me to construct this post.

    0 讨论(0)
  • 2020-11-28 08:58

    From RFC5208, the PKCS#8 unencrypted format consists of a PrivateKeyInfo structure:

    PrivateKeyInfo ::= SEQUENCE {
      version                   Version,
      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
      privateKey                PrivateKey,
      attributes           [0]  IMPLICIT Attributes OPTIONAL }
    

    where privateKey is:

    "...an octet string whose contents are the value of the private key. The interpretation of the contents is defined in the registration of the private-key algorithm. For an RSA private key, for example, the contents are a BER encoding of a value of type RSAPrivateKey."

    This RSAPrivateKey structure is just the PKCS#1 encoding of the key, which we can extract using BouncyCastle:

    // pkcs8Bytes contains PKCS#8 DER-encoded key as a byte[]
    PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes);
    RSAPrivateKeyStructure pkcs1Key = RSAPrivateKeyStructure.getInstance(
            pki.getPrivateKey());
    byte[] pkcs1Bytes = pkcs1Key.getEncoded(); // etc.
    
    0 讨论(0)
提交回复
热议问题