有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

加密从一个文件字符串创建java私钥和公钥

你好

还有另一个第三方需要我的web应用程序以加密格式向他们发送一些数据。所以他们给我发了一些指南,但是,我不熟悉,我试图用谷歌搜索,但看起来我用谷歌搜索错了方向

指南内容如下:

  1. 运行openssl命令以生成私钥:

    openssl ecparam-name prime256v1-genkey-out myprivate。pem

在运行这个命令之后,我输出了一个priv.pem文件,我看到里面有一个带有“==”的key end,如下所示:

-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILefWfeuZOgnbDlxpwo3uQ2xQXfhXHUPTS+vKzvVZdCToAoGCCqGSM49
AwEHoUQDQgAE4MeQspGRJ1qdpweBfiaT5P84alZdga1f7mSpa5HqXTH58u0ZWJUQ
J7ToU/bUOPITh4FX07AV6wrgFCmwtUenDQ==
-----END EC PRIVATE KEY-----
  1. 第二个是运行openssl命令生成公钥,然后发送它们:

    openssl ec-在myprivate中。pem-pubout-OutMyPublic。pem

  2. 将私钥转换为pkcs8格式:

    myprivate中的opensslpkcs8-topk8-nocrypt-。pem-out mypkcs8。pem

  3. 第三方会给我一个字符串格式的公钥,然后让我生成一个密钥,并提供一些java代码,如下所示:

第一个是生成密钥,第二个是加密:

public static SecretKey generateSharedSecret(PrivateKey privateKey,
            PublicKey publicKey) {
        try {
            KeyAgreement keyAgreement = KeyAgreement.getInstance( "ECDH" );
            keyAgreement.init( privateKey );
            keyAgreement.doPhase( publicKey, true );
            SecretKeySpec key = new SecretKeySpec(
                    keyAgreement.generateSecret( ), "AES" );
            return key;
        } catch ( Exception e ) {
            // TODO Auto-generated catch block
            e.printStackTrace( );
            return null;
        }
    }

public static String encryptString(SecretKey key, String plainText) {
        try {
            String myIv = "Testing @ IV!";
            byte[] iv = myIv.getBytes( "UTF-8" );
            IvParameterSpec ivSpec = new IvParameterSpec( iv );
            Cipher cipher = Cipher.getInstance( "AES / CBC / PKCS5Padding" );
            byte[] plainTextBytes = plainText.getBytes( "UTF-8" );
            byte[] cipherText;
            cipher.init( Cipher.ENCRYPT_MODE, key, ivSpec );
            cipherText = new byte[cipher.getOutputSize( plainTextBytes.length )];
            int encryptLength = cipher.update( plainTextBytes, 0,
                    plainTextBytes.length, cipherText, 0 );
            encryptLength += cipher.doFinal( cipherText, encryptLength );
            return bytesToHex( cipherText );
        } catch ( Exception e ) {
            e.printStackTrace( );
            return null;
        }
    }

以及字节到十六进制字符串的方法:

public static String bytesToHex(byte[] byteArray) {
    StringBuffer hexStringBuffer = new StringBuffer( );
    for ( int i = 0; i < byteArray.length; i++ ) {
        hexStringBuffer.append( String.format( "%02X", byteArray[ i ] ) );
    }
    return hexStringBuffer.toString( );
}

通过使用openssl命令,我有一个私钥和一个公钥,但第四步告诉我,他们也会给我一个公钥,因此我不知道应该使用哪个公钥

还有,如何将字符串转换为javaPrivateKeyPublicKey对象

*附加组件* 我尝试将der文件转换为java PublicKey对象,它看起来很有效。在此之前,我使用openssl命令将pem转换为der:

openssl pkey -pubin -in ecpubkey.pem -outform der -out ecpubkey.der

以下是java代码:

        File f = new File("/home/my/Desktop/key/ecpubkey.der");
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] keyBytes = new byte[(int) f.length()];
        dis.readFully(keyBytes);
        dis.close();

        KeyFactory fact = KeyFactory.getInstance("EC");
        PublicKey theirpub = fact.generatePublic(new X509EncodedKeySpec(keyBytes));

但是,当我尝试将der文件转换为java PrivateKey对象时,我遇到了java.security.spec.InvalidKeySpecException: java.io.IOException: insufficient data,我所做的是:

openssl ecparam -name prime256v1 -genkey -out priv.pem
openssl pkcs8 -topk8 -nocrypt -in priv.pem -outform der -out priv.der

下面是我的java代码:

        File f2 = new File("/home/my/Desktop/key/priv.der");
        FileInputStream fis2 = new FileInputStream(f2);
        DataInputStream dis2 = new DataInputStream(fis2);
        byte[] keyBytes2 = new byte[(int) f.length()];
        dis2.readFully(keyBytes2);
        dis2.close();

        KeyFactory fact2 = KeyFactory.getInstance("EC");
        PrivateKey pKey = fact2.generatePrivate( new PKCS8EncodedKeySpec(keyBytes2) ); // this line hit insufficient data

共 (2) 个答案

  1. # 1 楼答案

    Diffie Hellman在wikipedia中得到了很好的解释,这里可能有数百个Q和crypto中的一些。SX和安全。SX,关于它,但我不容易找到。简而言之:

    • 生成密钥对,保留私钥,并将公钥提供给另一方

    • 另一方做同样的事情(或其反映):生成一个密钥对,保留他们的私钥,并向您提供他们的公钥

    • 您可以使用私钥和他们的公钥来计算“协议”值

    • 他们同样使用私钥和你的公钥来计算相同的“协议”值。这也被称为共享秘密,因为你和另一方都知道,但任何偷听你流量的人都不知道

    该概要中的“提供”省略了许多非常重要的细节。重要的是,当你向另一方提供公钥时,他们实际上得到了你的公钥,而不是被对手更改或替换的值,同样,当他们向你提供公钥时,你得到的是真实的公钥,而不是经过修改或伪造的公钥。这是实际的DH系统主要发生故障的地方,而您在这里提到的任何保护措施或复杂性都表明,如果用于任何值得窃取的东西,您的方案将是不安全的,很容易被破坏

    注意:你不应该向任何人透露或“发送”你的私钥,他们也不应该同样地透露他们的私钥。这是公钥(或“非对称”)加密具有任何价值或用途的主要基础

    有很多方法可以表示密钥,但只有一些与您相关

    Public键通常以

    • ASN。1结构主题PublicKeyInfo在X.509中定义,在PKIX中更方便,主要在rfc5280 #4.1#4.1.2.7rfc3279 2.3中定义,在DER中编码,,它的局限性是,这种编码中使用的许多字节都不是有效字符,无法正确显示或以其他方式操作,有时甚至无法传输或存储

    • 这和ASN一样。1 DER结构以“PEM”格式“包装”,将麻烦的二进制数据转换为易于操作的所有可显示字符。PEM格式最初是为一个安全的电子邮件方案调用Privacy Enhanced Mail创建的,它已被其他方案和技术取代,但它定义的格式仍在使用。最近,{a6}重新标准化了公钥PEM格式(如您所见,参考rfc5280)

    OpenSSL支持这两种方式,但您使用的命令行实用程序大多默认为PEM,因为您需要将密钥传递给“他们”,他们也需要将密钥传递给您,所以PEM可能是最可靠和/或方便的方式。(虽然其他格式也是可能的,但如果你和他们同意,并且如果他们要求其他内容,那么你必须同意该方案才能起作用。)

    Java直接只支持DER,因此假设您在SPKI PEM中收到他们的公钥,要在Java中使用它,您需要将其转换为DER。您可以在OpenSSL中执行此操作

    openssl pkey -pubin -in theirpub.pem -outform der -out theirpub.der
    

    然后将DER读入Java加密密钥工厂:

    byte[] theirpubder = Files.readAllBytes(Paths.get(whatever));
    KeyFactory fact = KeyFactory.getInstance("EC");
    PublicKey theirpub = fact.generatePublic(new X509EncodedKeySpec(theirpubder));
    // can downcast to ECPublicKey if you want to be more specific
    

    或者,您可以让Java转换PEM,这并不太难;有几种变体,但我喜欢:

    String theirpubpem = new String(Files.readAllBytes(Paths.get(whatever)));
    // IN GENERAL letting new String(byte[]) default the charset is dangerous, but PEM is OK
    byte[] theirpubder = Base64.getMIMEDecoder().decode(theirpubpem.replaceAll("  -[^\\n]*\\n","") );
    // continue as for DER
    

    对于私人密钥 有更多的表示形式,但只有一种(或两种)是Java与OpenSSL共享的。由于您只需要在本地存储私钥,而不需要“发送”,因此可能不需要PEM;如果是这样,您只需将-outform der添加到pkcs8 -topk8 -nocrypt命令中,适当地调整名称,并以与上面相同的方式直接在Java KeyFactory中读取结果,除了使用PKCS8EncodedKeySpecgeneratePrivate[EC]PrivateKey。如果您确实想将其存储在(PKCS8 clear)PEM中,也可以将以上内容结合使用


    直接使用DH协议值作为对称密码(例如AES)密钥是不标准的,通常不被认为是良好的做法,尽管对于带有prime256v1(又名secp256r1或p-256)的ECDH,在技术上是可能的。AFAIK所有好的标准都在两者之间使用密钥派生步骤(又称密钥派生函数或KDF)。由于您还没有向我们展示他们的“指南”,我不能说这至少对正确的小值是正确的

    当然你知道,对于同一个密钥(在本例中是相同的DH结果)多次使用带有固定IV的CBC,是不安全的。我认为“测试”意味着你计划用更好的东西来取代它

    另外,仅供参考,您不需要使用Cipher.init,update,doFinalAPI的全部复杂性。当数据足够小,可以放入内存时,如这里所示,您可以执行以下操作:

    cipher.init(ENCRYPT_MODE, key, parms);
    byte[] encrypted = cipher.doFinal (plainbytes);
    // or since you want to hexify it
    ... bytesToHex (cipher.doFinal (plainbytes)) ...
    

    最后,因为Java byte是有符号的,所以bytesToHex将输出几乎一半的前缀为FFFFFF的字节。这是非常不寻常的,非常丑陋,但我也不知道这对你来说是否“正确”

  2. # 2 楼答案

    基于dave_thompson_085的解释和代码,我通过以下方式创建了我的java公钥和私钥:

    public static PublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException {
        String publicKeyPEM = getKey(filename);
        return getPublicKeyFromString(publicKeyPEM);
    }
    
    private static String getKey(String filename) throws IOException {
        // Read key from file
        String strKeyPEM = "";
        BufferedReader br = new BufferedReader(new FileReader(filename));
        String line;
        while ((line = br.readLine()) != null) {
            strKeyPEM += line + "\n";
        }
        br.close();
        return strKeyPEM;
    }
    
    public static PublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
        String publicKeyPEM = key;
        publicKeyPEM = publicKeyPEM.replace("  -BEGIN PUBLIC KEY  -\n", "");
        publicKeyPEM = publicKeyPEM.replace("  -END PUBLIC KEY  -", "");
        BASE64Decoder b = new BASE64Decoder();
        byte[] encoded = b.decodeBuffer(publicKeyPEM);
        KeyFactory kf = KeyFactory.getInstance("EC");
        PublicKey pubKey = (PublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
        return pubKey;
    }
    

    这是私钥

    public static PrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
        String privateKeyPEM = getKey(filename);
        return getPrivateKeyFromString(privateKeyPEM);
    }
    
    public static PrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
        String privateKeyPEM = key;
        privateKeyPEM = privateKeyPEM.replace("  -BEGIN PRIVATE KEY  -\n", "");
        privateKeyPEM = privateKeyPEM.replace("  -END PRIVATE KEY  -", "");
    
        BASE64Decoder b = new BASE64Decoder();
        byte[] encoded = b.decodeBuffer(privateKeyPEM);
        KeyFactory kf = KeyFactory.getInstance("EC");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        PrivateKey privKey = (PrivateKey) kf.generatePrivate(keySpec);
        return privKey;
    }
    

    非常感谢@dave_thompson_085的解释