将 tweetnacl.js 与 TweetNaclFast (java) 混合用于非对称加密
Posted
技术标签:
【中文标题】将 tweetnacl.js 与 TweetNaclFast (java) 混合用于非对称加密【英文标题】:Mixing tweetnacl.js with TweetNaclFast (java) for asymmetric encryption 【发布时间】:2020-12-22 22:38:13 【问题描述】:我们的项目使用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
加密的类似物是什么。是否支持?请注意,如果tweetnacl-java
不支持此功能,我将对java
或kotlin
开放。
【问题讨论】:
【参考方案1】: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>
【讨论】:
【参考方案2】:我的 tweetnacl-java 完整代码(感谢@topaco)
我生成了两个随机密钥对并将它们的密钥保存在 application.properties 文件中,这样,我将始终拥有相同的 pub&sec 以及 nonce。
KeyPair baseKeyPair= Box.keyPair(); String baseKeyPairSecretKey = Base64.getEncoder().encodeToString(baseKeyPair.getSecretKey());
KeyPair ephemeralKeyPair= Box.keyPair(); String ephemeralKeyPairSecretKey = Base64.getEncoder().encodeToString(ephemeralKeyPair.getSecretKey());
byte[] nonce = TweetNaclFast.randombytes(Box.nonceLength); String 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
【讨论】:
以上是关于将 tweetnacl.js 与 TweetNaclFast (java) 混合用于非对称加密的主要内容,如果未能解决你的问题,请参考以下文章
php [将产品与社交共享插件集成]将社交共享插件与WooCommerce集成 - Sharedaddy
php [将产品与社交共享插件集成]将社交共享插件与WooCommerce集成 - Sharedaddy
php [将产品与社交共享插件集成]将社交共享插件与WooCommerce集成 - Sharedaddy