Смешивание tweetnacl.js с TweetNaclFast (java) для асимметричного шифрования
В нашем проекте используется асимметричное шифрование с nacl.box
и эфемерные ключи:
encrypt(pubKey, msg) {
if (typeof msg !== 'string') {
msg = JSON.stringify(msg)
}
let ephemKeys = nacl.box.keyPair()
let msgArr = nacl.util.decodeUTF8(msg)
let nonce = nacl.randomBytes(nacl.box.nonceLength)
p(`naclRsa.pubKey=${this.pubKey}`)
let encrypted = nacl.box(
msgArr,
nonce,
nacl.util.decodeBase64(pubKey),
ephemKeys.secretKey
)
let nonce64 = nacl.util.encodeBase64(nonce)
let pubKey64 = nacl.util.encodeBase64(ephemKeys.publicKey)
let encrypted64 = nacl.util.encodeBase64(encrypted)
return {nonce: nonce64, ephemPubKey: pubKey64, encrypted: encrypted64}
}
В настоящее время у нас есть node.js
приложения, которые затем расшифровывают эти сообщения. Мы хотели бы использоватьjvm
языки для некоторых функций. Кажется, нет богатства устоявшихся игроков дляtweet-nacl
на jvm
но кажется
tweetnacl-java
https://github.com/InstantWebP2P/tweetnacl-java
и его рекомендуемая реализация
° tweetnacl-fast
https://github.com/InstantWebP2P/tweetnacl-java/blob/master/src/main/java/com/iwebpp/crypto/TweetNaclFast.java
были популярны.
Непонятно, какой аналог asymmetric
шифрование с помощью эфемерных ключей было в этой библиотеке. Поддерживается? Обратите внимание, что я был бы открыт для любогоjava
или kotlin
если бы это не поддерживалось в tweetnacl-java
.
2 ответа
tweetnacl-java - это порт tweetnacl-js. Следовательно, следует ожидать, что оба они будут обеспечивать одинаковую функциональность. По крайней мере, для опубликованного метода это так, что может быть реализовано на стороне Java с помощью TweetNaclFast следующим образом:
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import com.iwebpp.crypto.TweetNaclFast;
import com.iwebpp.crypto.TweetNaclFast.Box;
import com.iwebpp.crypto.TweetNaclFast.Box.KeyPair;
...
private static EncryptedData encrypt(byte[] pubKey, String msg) {
KeyPair ephemKeys = Box.keyPair();
byte[] msgArr = msg.getBytes(StandardCharsets.UTF_8);
byte[] nonce = TweetNaclFast.randombytes(Box.nonceLength);
Box box = new Box(pubKey, ephemKeys.getSecretKey());
byte[] encrypted = box.box(msgArr, nonce);
String nonce64 = Base64.getEncoder().encodeToString(nonce);
String ephemPubKey64 = Base64.getEncoder().encodeToString(ephemKeys.getPublicKey());
String encrypted64 = Base64.getEncoder().encodeToString(encrypted);
return new EncryptedData(nonce64, ephemPubKey64, encrypted64);
}
...
class EncryptedData {
public EncryptedData(String nonce, String ephemPubKey, String encrypted) {
this.nonce = nonce;
this.ephemPubKey = ephemPubKey;
this.encrypted = encrypted;
}
public String nonce;
public String ephemPubKey;
public String encrypted;
}
Чтобы продемонстрировать, что обе стороны совместимы, в дальнейшем открытый текст зашифрован на стороне Java и дешифрован на стороне JavaScript:
Во-первых, на стороне JavaScript необходима пара ключей, открытый ключ которой (
publicKeyJS
) передается на сторону Java. Пара ключей на стороне JavaScript может быть сгенерирована следующим образом:let keysJS = nacl.box.keyPair(); let secretKeyJS = keysJS.secretKey; let publicKeyJS = keysJS.publicKey; console.log("Secret key: " + nacl.util.encodeBase64(secretKeyJS)); console.log("Public key: " + nacl.util.encodeBase64(publicKeyJS));
со следующим образцом вывода:
Secret key: YTxAFmYGm4yV2OP94E4pcD6LSsN4gcSBBAlU105l7hw= Public key: BDXNKDHeq0vILm8oawAGAQtdIsgwethzBTBqmsWI+R8=
Затем для шифрования на стороне Java используется
encrypt
метод, опубликованный выше (иpublicKeyJS
):byte[] publicKeyJS = Base64.getDecoder().decode("BDXNKDHeq0vILm8oawAGAQtdIsgwethzBTBqmsWI+R8="); EncryptedData encryptedFromJava = encrypt(publicKeyJS, "I've got a feeling we're not in Kansas anymore..."); System.out.println("Nonce: " + encryptedFromJava.nonce); System.out.println("Ephemeral public key: " + encryptedFromJava.ephemPubKey); System.out.println("Ciphertext: " + encryptedFromJava.encrypted);
со следующим образцом вывода:
Nonce: FcdzXfYwSbI0nq2WXsLe9aAh94vXSoWd Ephemeral public key: Mde+9metwF1jIEij5rlZDHjAStR/pd4BN9p5JbZleSg= Ciphertext: hHo7caCxTU+hghcFZFv+djAkSlWKnC12xj82V2R/Iz9GdOMoTzjoCDcz9m/KbRN6i5dkYi3+Gf0YTtKlZQWFooo=
Расшифровка на стороне JS дает исходный открытый текст (с использованием
secretKeyJS
):let nonce = "FcdzXfYwSbI0nq2WXsLe9aAh94vXSoWd"; let ephemPubKey = "Mde+9metwF1jIEij5rlZDHjAStR/pd4BN9p5JbZleSg="; let encrypted = "hHo7caCxTU+hghcFZFv+djAkSlWKnC12xj82V2R/Iz9GdOMoTzjoCDcz9m/KbRN6i5dkYi3+Gf0YTtKlZQWFooo="; let secretKeyJS = nacl.util.decodeBase64("YTxAFmYGm4yV2OP94E4pcD6LSsN4gcSBBAlU105l7hw="); let decryptedFromJS = decrypt(secretKeyJS, {nonce: nonce, ephemPubKey: ephemPubKey, encrypted: encrypted}); console.log(nacl.util.encodeUTF8(decryptedFromJS)); // I've got a feeling we're not in Kansas anymore... function decrypt(secretKey, ciphertext){ let decrypted = nacl.box.open( nacl.util.decodeBase64(ciphertext.encrypted), nacl.util.decodeBase64(ciphertext.nonce), nacl.util.decodeBase64(ciphertext.ephemPubKey), secretKey ); return decrypted; }
<script src="https://cdn.jsdelivr.net/npm/tweetnacl-util@0.15.1/nacl-util.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/tweetnacl@1.0.3/nacl.min.js"></script>
Мой полный код для tweetnacl-java (Престижность @topaco)
Я сгенерировал две случайные пары ключей и сохранил их секретные ключи в файле application.properties, так что у меня всегда будет один и тот же pub& sec вместе с nonce.
KeyPair baseKeyPair= Box.keyPair(); Строка baseKeyPairSecretKey = Base64.getEncoder (). EncodeToString(baseKeyPair.getSecretKey());
KeyPair ephemeralKeyPair= Box.keyPair(); Строка ephemeralKeyPairSecretKey = Base64.getEncoder (). EncodeToString(ephemeralKeyPair.getSecretKey());
byte[] nonce = TweetNaclFast.randombytes(Box.nonceLength); Строка nonce64 = Base64.getEncoder (). EncodeToString(nonce);
private final AppConfig config; //you can autowire the config class
private TweetNaclFast.Box.KeyPair getBaseKeyPair() {
byte[] secretKey = Base64.getDecoder().decode(config.getTweetNACLConfig().getBaseSecretKey());
return TweetNaclFast.Box.keyPair_fromSecretKey(mySecretKey);
}
private TweetNaclFast.Box.KeyPair getEphemeralKeyPair() {
byte[] secretKey = Base64.getDecoder().decode(config.getTweetNACLConfig().getEphemeralSecretKey());
return TweetNaclFast.Box.keyPair_fromSecretKey(mySecretKey);
}
private byte[] getNonce() {
return Base64.getDecoder().decode(config.getTweetNACLConfig().getNonce().getBytes(StandardCharsets.UTF_8));
}
public String encrypt(String msg) {
TweetNaclFast.Box.KeyPair baseKeyPair = getBaseKeyPair();
TweetNaclFast.Box.KeyPair ephemeralKeyPair = getEphemeralKeyPair();
byte[] msgArr = msg.getBytes(StandardCharsets.UTF_8);
byte[] nonce = getNonce();
TweetNaclFast.Box box = new TweetNaclFast.Box(baseKeyPair.getPublicKey(), ephemeralKeyPair.getSecretKey());
byte[] encryptedData = box.box(msgArr, nonce);
return Base64.getEncoder().encodeToString(encryptData);
}
public String decrypt(String encryptedData) {
TweetNaclFast.Box.KeyPair baseKeyPair = getBaseKeyPair();
TweetNaclFast.Box.KeyPair ephemeralKeyPair = getEphemeralKeyPair();
byte[] nonce = getNonce();
TweetNaclFast.Box box = new TweetNaclFast.Box(ephemeralKeyPair.getPublicKey(), baseKeyPair.getSecretKey());
byte[] boxToOpen = Base64.getDecoder().decode(encryptedData);
byte[] decryptedData = box.open(boxToOpen, nonce);
return new String(decryptedData, StandardCharsets.UTF_8);
}
> Please, note these two lines
> TweetNaclFast.Box box = new TweetNaclFast.Box(baseKeyPair.getPublicKey(), ephemeralKeyPair.getSecretKey());
> TweetNaclFast.Box box = new TweetNaclFast.Box(ephemeralKeyPair.getPublicKey(), baseKeyPair.getSecretKey());
return encryptAndDecryptData.encrypt("Friday"); // JHo/tk/Jpp2rpxpzIIgBhVhK/CBZLg==
return encryptAndDecryptData.decrypt("JHo/tk/Jpp2rpxpzIIgBhVhK/CBZLg==") //Friday