我正在尝试对使用 X25519 算法的密钥推导生成的密码进行密文解密。虽然我能够使用 Java 系统中生成的私钥进行解密,但它在 python 上失败了。 Java 代码使用 generate secret 和 tlspremaster,这是我在 python 上找不到的东西。有人可以建议如何解决吗?我会在这里发布我的代码。
我对密码学还很陌生,还在学习。
下面是我的代码。 pkey 是我在 Mac 主机上本地生成的私钥。密钥交换后,我将派生一个对称密钥来解密从 Java 收到的加密字符串。这些密钥是在我的 Mac 主机上生成的 X25519 密钥,其他系统的公钥以 DER 编码格式返回。在 Java 中使用我的公钥和远程私钥解密相同的有效负载效果很好。然而,同样的反过来是行不通的。我在下面做错了什么?
from cryptography.hazmat.primitives import serialization
import base64
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
pkey = serialization.load_der_private_key(
base64.b64decode("MC4CAQAwBQYDK2VuBCIEIKh1Dq7Fu82lqQdBQJTHTvBTxtD6hLconopqvVLVy81s"),
password=None
)
ukey=pkey.public_key()
encstr = base64.b64decode("tAIfdjkAClrwWFcKVYMiCYVhm7NFAotPyBgF2YJkM2ETWPEYcGG6g37MivEhS8b5".encode('utf-8'))
uenkey = ukey.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
penkey = pkey.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
ondcpub = (base64.b64decode("MCowBQYDK2VuAyEAa9Wbpvd9SsrpOZFcynyt/TO3x0Yrqyys4NUGIvyxX2Q="))
oenkey = serialization.load_der_public_key(ondcpub)
shared_key = pkey.exchange(oenkey)
shkey = base64.urlsafe_b64encode(shared_key).decode('utf-8')
print("Shared Secret: ", base64.b64encode(shared_key).decode('utf-8'))
hkdf = HKDF(
algorithm=hashes.BLAKE2b(64),
length=32,
salt=None,
info=None
)
key = hkdf.derive(shared_key)
print("Derived Secret: ", base64.b64encode(key).decode('utf-8'))
iv = encstr[0:16]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
dec = cipher.decryptor()
text = dec.update(encstr) + dec.finalize()
但是,在打印文本时,我返回的是垃圾字节,而不是我加密的内容。
下面是能够正确解密字符串而没有问题的 Java 代码:
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
//
import org.springframework.stereotype.Component;
import lombok.Data;
@Component
@Data
public class SubscribeEncryptDecrypt {
String clientPrivateKey = "MFECAQEwBQYDK2VuBCIEIChY69PwPeovw1zAh7TRU+E40LIEykBsbIBp3CanVvRegSEASfWOME2kQQ75i5iMHx0ZodBn0P9UTHcOkeczDmeOVkU=";
String clientPublicKey = "MCowBQYDK2VuAyEASfWOME2kQQ75i5iMHx0ZodBn0P9UTHcOkeczDmeOVkU=";
String proteanPublicKey = "MCowBQYDK2VuAyEALtPj74XkIrkyxTqyssjtYJ3KRND5FnzK5MDrwlK3kC8=";
String proteanPrivateKey = "MFECAQEwBQYDK2VuBCIEIAj5U1DVAX5eGI1jIIcjmzWgPQlIg/T1Q6A3pZ0AIWp6gSEAJGnKRTAEcSvpgD0mw9gBHv94E3w8sTtmPlszuXIEAF0=";
public static String secretKey = "TlsPremasterSecret";
public static void setup() {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
public String decrypt(String clientPrivateKey, String proteanPublicKey, String value) {
try {
byte[] dataBytes = Base64.getDecoder().decode(proteanPublicKey);
PublicKey publicKey = getPublicKey("X25519", dataBytes);
dataBytes = Base64.getDecoder().decode(clientPrivateKey);
PrivateKey privateKey = getPrivateKey("X25519", dataBytes);
KeyAgreement atServer1 = KeyAgreement.getInstance("X25519", BouncyCastleProvider.PROVIDER_NAME);
atServer1.init(privateKey); // Server1 uses its private key to initialize the aggreement object
atServer1.doPhase(publicKey, true); // Uses Server2's ppublic Key
SecretKey key1 = atServer1.generateSecret(secretKey); // derive secret at server 1.
// "TlsPremasterSecret" is the algorithm for
Cipher cipher2 = Cipher.getInstance("AES", BouncyCastleProvider.PROVIDER_NAME);
cipher2.init(Cipher.DECRYPT_MODE, key1); // Same derived key in server 2same as key1
byte[] decrypted2 = cipher2.doFinal(Base64.getDecoder().decode(value)); // b64 decode the
// message before
return new String(decrypted2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
public PublicKey getPublicKey(String algo, byte[] jceBytes) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(jceBytes);
PublicKey key = KeyFactory.getInstance(algo, BouncyCastleProvider.PROVIDER_NAME)
.generatePublic(x509EncodedKeySpec);
return key;
}
public PrivateKey getPrivateKey(String algo, byte[] jceBytes) throws Exception {
PrivateKey key = KeyFactory.getInstance(algo, BouncyCastleProvider.PROVIDER_NAME)
.generatePrivate(new PKCS8EncodedKeySpec(jceBytes));
return key;
}
public String encrypt(String clientPublicKey, String proteanPrivateKey, String value) {
try {
byte[] dataBytes = Base64.getDecoder().decode(clientPublicKey);
PublicKey publicKey = getPublicKey("X25519", dataBytes);
dataBytes = Base64.getDecoder().decode(proteanPrivateKey);
PrivateKey privateKey = getPrivateKey("X25519", dataBytes);
KeyAgreement atServer1 = KeyAgreement.getInstance("X25519", BouncyCastleProvider.PROVIDER_NAME);
atServer1.init(privateKey); // Server1 uses its private key to initialize the aggreement object
atServer1.doPhase(publicKey, true); // Uses Server2's ppublic Key
SecretKey key1 = atServer1.generateSecret(secretKey); // //derive secret at server 1.
// "TlsPremasterSecret" is the algorithm for
// *Server1
Cipher cipher1 = Cipher.getInstance("AES", BouncyCastleProvider.PROVIDER_NAME);
cipher1.init(Cipher.ENCRYPT_MODE, key1);
byte[] encrypted1 = cipher1.doFinal(value.getBytes(StandardCharsets.UTF_8));
String b64Encryped1 = Base64.getEncoder().encodeToString(encrypted1);
return b64Encryped1;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
public static void main(String[] args) {
SubscribeEncryptDecrypt decr = new SubscribeEncryptDecrypt();
decr.setup();
String enc = decr.encrypt("MCowBQYDK2VuAyEAr+B5vDW7a3QFkCcYqf3RMMi5BxsnwU3fy63pxpCDnQM=",
"MFECAQEwBQYDK2VuBCIEIJDIsJi4nLGZ7BKaJkkIzJxubIndEOvT5hx0MKgoGYFvgSEA13ZQjiRLAA5YG6prELnmQwboQlpj0MzI94XF/kG4UmY=",
"Fossgen is awesome!");
System.out.println("ENC:");
System.out.println(enc);
System.out.println("DECR3:");
System.out.println(decr.decrypt("MFECAQEwBQYDK2VuBCIEIEhk0BhCxAEIHg8HehKzo9IHmCFRpcp9IlBGYsWTPdVzgSEAMmKN2FKRf+ojfYYQ80ZcgyJQvQlbxDA8BAsxNG4vuGI=",
"MCowBQYDK2VuAyEA13ZQjiRLAA5YG6prELnmQwboQlpj0MzI94XF/kG4UmY=",
enc));
}
}
代码中提到的键是真实的。