在Java中将Ed25519公钥编码为SSH格式

eqoofvh9  于 2023-11-15  发布在  Java
关注(0)|答案(1)|浏览(167)

开始,我是新的密码学和不同类型的密钥/编码/格式,如果我说错了,请纠正我。我有一个Java应用程序需要将Ed 25519密钥保存到密钥库。该应用程序是遗留的,一些方法和库无法更改。它使用Apache MINA SSHD和BouncyCastle将密钥存储在密钥库中,并将公钥编码为SSH格式。RSA和DSA密钥没有问题。问题是Apache MINA使用了EdDSA密钥的net.i2p实现,而BouncyCastle使用了自己的BCEdDSA密钥。我们使用的一个方法来自MINA,并返回net.i2p密钥,因为我在将其保存到密钥库时遇到了麻烦,因为BouncyCastle的JCAContentSigner无法识别密钥的这种实现。问题出现在对公钥进行编码时。
我创建了一个实现ContentSigner的类,并使用net.i2p的EdDSAEngine类来创建签名器。

private class EdDSAContentSigner implements ContentSigner
    {
        private final AlgorithmIdentifier sigAlgId;
        private final PrivateKey privateKey;
        private ByteArrayOutputStream stream;

        public EdDSAContentSigner(AlgorithmIdentifier sigAlgId, PrivateKey privKey)
        {
            this.sigAlgId = sigAlgId;
            this.privateKey = privKey;
            this.stream = new ByteArrayOutputStream();
        }

        @Override
        public AlgorithmIdentifier getAlgorithmIdentifier()
        {
            return sigAlgId;
        }

        @Override
        public OutputStream getOutputStream()
        {
            stream.reset();
            return stream;
        }

        @Override
        public byte[] getSignature()
        {
            byte[] dataToSign = stream.toByteArray();
            try
            {
                EdDSAEngine sig = new EdDSAEngine();
                sig.initSign(privateKey);
                return sig.signOneShot(dataToSign);
            }
            catch (GeneralSecurityException e)
            {
                LOG.error("Cannot sign data : " + e.getMessage(), e);
                throw new IllegalStateException("Cannot sign data : " + e.getMessage(), e);
            }
        }
    }

字符串
然后我设法创建了一个证书并将密钥保存到密钥库。该应用程序具有上传私钥文件的功能,它以SSH格式生成公钥-从ssh-ed 25519开始。
现在我有一个问题,将公钥编码为SSH格式。即使我使用相同的私钥文件,它总是与ssh-keygen工具生成的公钥不同。同样,公钥可以是不同的类型net.i2p/BouncyCastle/sun.security.ec.ed. EdDSA PublicKeyImpl,这取决于什么方法调用我的encodeEdDSA PublicKey方法。我尝试了不同的方法来编码它,从调用.getEncoded()方法对PublicKey本身使用BouncyCastle的ASN 1 InputStream这是我最新的方法.我还将提供RSA密钥编码方法女巫返回相同的编码作为ssh-keygen工具.我希望能够使用公共密钥连接到一个服务器使用腻子.任何帮助/建议将不胜感激.

private static String encodeEdDSAPublicKey(PublicKey publicKey)
        throws IOException
    {
       try(ASN1InputStream asn1InputStream = new ASN1InputStream(publicKey.getEncoded()))
        {
            ASN1Primitive primitive = asn1InputStream.readObject();
            byte[] keyBytes ((ASN1Sequence)primitive).getObjectAt(1).toASN1Primitive().getEncoded();
          ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
          DataOutputStream dos = new DataOutputStream(byteOs);
          dos.writeInt("ssh-ed25519".getBytes().length);
          dos.write("ssh-ed25519".getBytes());
          dos.writeInt(keyBytes.length);
          dos.write(keyBytes);
          return Base64.getEncoder().encodeToString(byteOs.toByteArray());
        }
    }
private static String encodeRSAPublicKey(PublicKey publicKey)
        throws IOException
    {
        String publicKeyEncoded;
        RSAPublicKey rsaPublicKey = (RSAPublicKey)publicKey;
        ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(byteOs);
        dos.writeInt("ssh-rsa".getBytes().length);
        dos.write("ssh-rsa".getBytes());
        dos.writeInt(rsaPublicKey.getPublicExponent().toByteArray().length);
        dos.write(rsaPublicKey.getPublicExponent().toByteArray());
        dos.writeInt(rsaPublicKey.getModulus().toByteArray().length);
        dos.write(rsaPublicKey.getModulus().toByteArray());
        publicKeyEncoded = Base64.getEncoder().encodeToString(byteOs.toByteArray());
        return publicKeyEncoded;
    }

这是我根据算法选择正确编码方法的方法

public static String encodePublicKey(PublicKey publicKey, String name)
        throws IOException
    {
        String suffix = "";
        String algorithm = publicKey.getAlgorithm();

        if (name != null)
        {
            suffix = name);
        }

        switch (algorithm)
        {
        case "RSA":
            return "ssh-rsa " + encodeRSAPublicKey(publicKey) + suffix;
        case "DSA":
            return "ssh-dss " + encodeDSAPublicKey(publicKey) + suffix;
        case "Ed25519":
        case "EdDSA":
            return "ssh-ed25519 " + encodeEdDSAPublicKey(publicKey) + suffix;
        default:
            throw new IOException("Unknown public key encoding: " + publicKey.getAlgorithm());
        }
    }

xtfmy6hx

xtfmy6hx1#

你非常接近了。Java PublicKey.getEncoded()是一个SPKI structure,它的字段1是一个BIT STRING *,包含 * 算法特定的数据,对于EdDSA来说,它是原始点编码。用途:

ASN1Object spki = new ASN1InputStream(pubkey.getEncoded()) .readObject();
    // or wrapped in the try as you have it is slightly cleaner
    byte[] point = ((ASN1BitString) ((ASN1Sequence)spki).getObjectAt(1) ).getOctets();

字符串
或者使用特定于类型的Bouncy类来处理解析和类型转换:

byte[] point = SubjectPublicKeyInfo.getInstance(pubkey.getEncoded()).getPublicKeyData().getOctets();


或者,如果使用Bouncy提供程序(和密钥类),则更简单地使用用途:

byte[] point = ((org.bouncycastle.jcajce.interfaces.EdDSAPublicKey)pubkey).getPointEncoding();
    // or org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey


在任何情况下,将point的长度和内容写入SSH格式。

相关问题