我想从另一个OutputStream
创建一个OutputStream
,其中新的OutputStream
将自动加密我写入该OutputStream
的内容。我想使用Bouncy Castle,因为我已经在为其他功能使用该依赖项。
我在互联网上看到各种问题如何用Bouncy Castle加密数据,但答案要么是加密给定的文件
(我不使用文件,我使用OutputStream
),要么是需要复制粘贴大量代码。真不敢相信一定有那么难。
这是我的设置:
我注释掉了一些代码,密码上的init函数(代码编译了,但是测试失败了)。我不知道应该在init函数中放入什么作为第二个参数。读取的函数来自:https://github.com/jordanbaucke/pgp-sign-and-encrypt/blob/472d8932df303d6861ec494a3e942ea268eaf25f/src/signandencrypt.java#l272。只有testencryptdecryptithoutsigning是由我编写的。
代码:
@Test
void testEncryptDecryptWithoutSigning() throws Exception {
// The data will be written to this property
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Security.addProvider(new BouncyCastleProvider());
PGPSecretKey privateKey = readSecretKey(pathToFile("privatekey0"));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
//cipher.init(Cipher.ENCRYPT_MODE, privateKey);
CipherOutputStream os = new CipherOutputStream(baos, cipher);
// I also need to use a PrintWriter
PrintWriter printWriter =
new PrintWriter(new BufferedWriter(new OutputStreamWriter(
os,
StandardCharsets.UTF_8.name())));
// This is an example of super secret data to write
String data = "Some very sensitive data";
printWriter.print(data);
printWriter.close();
// At this point, the data is 'inside' the byte array property
// Assert the text is encrypted
if (baos.toString(StandardCharsets.UTF_8.name()).equals(data)) {
throw new RuntimeException("baos not encrypted");
}
PGPSecretKey publicKey = readSecretKey(pathToFile("publickey0"));
//cipher.init(Cipher.DECRYPT_MODE, publicKey);
ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
ByteArrayOutputStream decrypted = new ByteArrayOutputStream();
// Decrypt the stream, but how?
if (!decrypted.toString(StandardCharsets.UTF_8.name()).equals(data)) {
throw new RuntimeException("Not successfully decrypted");
}
}
static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException
{
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
Iterator keyRingIter = pgpSec.getKeyRings();
while (keyRingIter.hasNext())
{
PGPSecretKeyRing keyRing = (PGPSecretKeyRing)keyRingIter.next();
Iterator keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext())
{
PGPSecretKey key = (PGPSecretKey)keyIter.next();
if (key.isSigningKey())
{
return key;
}
}
}
throw new IllegalArgumentException("Can't find signing key in key ring.");
}
static PGPSecretKey readSecretKey(String fileName) throws IOException, PGPException
{
InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
PGPSecretKey secKey = readSecretKey(keyIn);
keyIn.close();
return secKey;
}
static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException
{
InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
PGPPublicKey pubKey = readPublicKey(keyIn);
keyIn.close();
return pubKey;
}
/**
* A simple routine that opens a key ring file and loads the first available key
* suitable for encryption.
*
* @param input data stream containing the public key data
* @return the first public key found.
* @throws IOException
* @throws PGPException
*/
static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException
{
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
Iterator keyRingIter = pgpPub.getKeyRings();
while (keyRingIter.hasNext())
{
PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next();
Iterator keyIter = keyRing.getPublicKeys();
while (keyIter.hasNext())
{
PGPPublicKey key = (PGPPublicKey)keyIter.next();
if (key.isEncryptionKey())
{
return key;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
首先,网站不会生成一个键区,而是生成三个键区。历史上,在PGP中,在实际的加密密钥和密钥与PGP用户所称的密钥之间一直存在一些歧义,因为对于给定的用户(或实体或角色等)来说,具有一个“主”或“主”密钥以及与该主密钥绑定的一个或多个子密钥是常见的。对于DSA+ELG密钥,技术上需要使用子密钥(而不是主密钥)进行加密;对于RSA来说,这样做被认为是良好的做法,因为单独管理(例如可能撤销)这些密钥通常更好。有些人还认为使用子密钥而不是主密钥来签名数据是很好的做法,并且只使用主密钥来签名密钥(PGP称之为Certificing-C),但有些人不这样做。当PGP用户和文档谈到一个“密钥”时,他们通常指的是一个主密钥及其(所有)子密钥的组,他们说主密钥或子密钥(或加密子密钥或签名子密钥)是指一个特定的实际密钥。
当您选择RSA时,该网站生成一个主密钥(keypair),用法为SCEA--即所有目的--和两个子密钥,每个子密钥的用法为SEA--所有目的对一个子密钥有效。这是荒谬的;如果masterkey支持签名和加密,大多数PGP程序将永远不会使用任何子密钥,即使它没有或您重写了它,子密钥之间也没有任何意义的区别,也没有逻辑方法来选择使用哪一个子密钥。
BouncyCastle通过改变术语来加剧这种情况:大多数PGP程序使用key表示实际密钥或主密钥加子密钥的组,如上所示,'public‘和'secret’key表示每个密钥或组的一半,'keyring'表示您存储的所有密钥组,通常存储在一个文件中,该文件可能用于许多不同的人或实体。然而,Bouncy将一个主密钥及其子密钥(以公开或秘密形式)的组称为KeyRing,将可能包含多个组的文件称为KeyRinghtml" target="_blank">Collection,这两个组都是公开或秘密的变体。不管怎样...
你的第一个问题是你把它倒过来了。在公钥密码学中,我们用公钥(一半)加密,用私钥(一半)解密,PGP(因此BCPG)称之为秘密。此外,由于PGP中的私有/秘密密钥是密码加密的,所以要使用它,我们必须首先解密它。(在JKS和PKCS12这样的“普通”JCA密钥库中也是如此,但在其他密钥库中则不一定。)
你的第二个问题是类型。虽然给定非对称算法的(特定)PGP密钥在语义上只是该算法的密钥,加上一些元数据(标识、首选项和信任/签名信息),但PGP密钥的BCPG中的Java对象(类)不在Java加密体系结构(JCA)中用于密钥的对象的类型层次结构中。用更简单的话说,org.bouncycastle.openpgp.pgppublickey
不是java.security.publickey
的子类。因此必须将这些关键对象转换为与JCA兼容的对象,以便与JCA一起使用。
通过这些更改和一些添加,以下代码可以工作(FSVO工作):
static void SO66155608BCPGPRawStream (String[] args) throws Exception {
byte[] plain = "testdata".getBytes(StandardCharsets.UTF_8);
PGPPublicKey p1 = null;
FileInputStream is = new FileInputStream (args[0]);
Iterator<PGPPublicKeyRing> i1 = new JcaPGPPublicKeyRingCollection (PGPUtil.getDecoderStream(is)).getKeyRings();
for( Iterator<PGPPublicKey> j1 = i1.next().getPublicKeys(); j1.hasNext(); ){
PGPPublicKey t1 = j1.next();
if( t1.isEncryptionKey() ){ p1 = t1; break; }
}
is.close();
if( p1 == null ) throw new Exception ("no encryption key");
PublicKey k1 = new JcaPGPKeyConverter().getPublicKey(p1);
Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c1.init(Cipher.ENCRYPT_MODE, k1);
ByteArrayOutputStream b1 = new ByteArrayOutputStream();
CipherOutputStream s1 = new CipherOutputStream(b1,c1);
s1.write(plain);
s1.close();
byte[] cipher = b1.toByteArray();
long id = p1.getKeyID();
System.out.println("keyid="+Long.toString(id,16)+" "+Arrays.toString(cipher));
if( Arrays.equals(cipher,plain) ) throw new Exception ("didn't encrypt!");
PGPSecretKey p2 = null;
is = new FileInputStream (args[1]);
Iterator<PGPSecretKeyRing> i2 = new JcaPGPSecretKeyRingCollection (PGPUtil.getDecoderStream(is)).getKeyRings();
for( Iterator<PGPSecretKey> j2 = i2.next().getSecretKeys(); j2.hasNext(); ){
PGPSecretKey t2 = j2.next();
if( t2.getKeyID() == id ){ p2 = t2; break; }
}
is.close();
if( p2 == null ) throw new Exception ("no decryption key");
PGPPrivateKey p3 = p2.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().build(args[2].toCharArray()));
PrivateKey k2 = new JcaPGPKeyConverter().getPrivateKey(p3);
Cipher c2 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c2.init(Cipher.DECRYPT_MODE, k2);
ByteArrayInputStream b2 = new ByteArrayInputStream(cipher);
CipherInputStream s2 = new CipherInputStream(b2,c2);
byte[] back = new byte[cipher.length]; // definitely more than needed
int actual = s2.read(back);
s2.close();
System.out.println ("Result->" + new String(back,0,actual,StandardCharsets.UTF_8));
}
(我发现将代码放在执行顺序的一个位置会更清楚,但您可以将其分解为多个片段,而不做任何实质性更改。)
我保留了您的逻辑(从Bouncy示例中),从第一组中选择第一个具有加密能力的公钥,无论是master还是sub,其中每个组都有一个Bouncy错误调用密钥环;由于以上所述的网站提供了主密钥SCEA,这始终是主密钥。不可能根据是否允许加密来选择一个秘密/私钥,而且在任何情况下都不能保证公钥文件总是按照相同的顺序,因此选择解密密钥的正确方法是匹配用于加密的密钥中的keyid。
而且,现代加密算法(不对称的,如RSA和对称的,如AES或“3DES”)产生的数据是任意的位模式,特别是大多数不是有效的UTF-8,所以将这些字节“解码”为UTF-8以与明文进行比较通常会破坏您的数据;如果您想要这个(不必要的)检查,您应该比较字节数组,如我所示。
最后,如果您不知道,通常不使用非对称算法来加密大型或可变大小的数据,这是您通常使用Java流的目的;这一点在维基百科的文章中也有解释。这种方法,直接使用RSA PKCS1-v1_5,并使用1024位密钥,只能处理117字节的数据(可能少于117个字符,视情况而定)。
如果您希望结果与任何实际的PGP实现兼容或可互操作,那肯定不是--这意味着从PGP密钥格式转换的工作是白费的,因为您可以按照Oracle网站上的基本教程或Stack上的数百个示例直接生成JCA格式的密钥。如果要与GPG或类似的方式互操作,则需要使用BCPG类进行PGP格式的加密和解密,这些类可以在普通字节流上分层,但与JCA的cipher{Input,Output}stream
完全不同且不兼容。
在阅读dagger 2的文档时,我找不到一种简单的方法来在构建测试应用时提供依赖项。我找到的唯一线索是: 匕首2不支持覆盖。覆盖简单测试假货的模块可以创建模块的子类来模拟该行为。应该分解使用覆盖并依赖依赖依赖项注入的模块,以便将被覆盖的模块表示为两个模块之间的选择。 我不明白我将如何在Android上设置这样的配置,任何人都可以解释吗?
我正在使用maven shade插件创建一个胖罐子,其中也包括一些弹性城堡罐子。但这造成了问题,因为Bouncy Castle的未签名版本。
我有一个库,我称之为,它是另一个项目的依赖项,名为。在的构建中。gradle,我正在使用,它以如下方式指定为依赖项: 这很有效-已编译。但是,当我在我的项目中使用它时,我得到了以下内容: <code>生成。的gradle如下所示: 可能这是因为库在,但它没有正确的位置来搜索Maven存储库。有没有一种方法可以使这些搜索位置以及依赖关系本身变得短暂?
我正在使用Gradle插件0.9和Android Studio 0.5.4。 我有一个Android库项目,只有一个依赖项。jar位于/libs文件夹中。当我在Proguard激活的情况下运行AssemblerRelease任务时,生成的AAR具有以下结构: 我的图书馆 问题是myJar。jar类被合并到类中。jar by Proguard(当我在没有Proguard的情况下运行时,它不会发生)。
我想将android库构建为AAR文件,其中包括输出AAR文件中的依赖项。它使用另一个AAR文件作为libs文件夹中的依赖项。 生成库时显示此错误: 我的程序规则。pro文件是 我也试着使用https://github.com/kezong/fat-aar-android库并将facetec sdk模块作为嵌入项目导入,但build命令显示lint错误
问题内容: 首先,我要说我是Ant的新手。就像之前一样,我只是在两天前才开始学习它以完成此任务。 我想要做的是创建一个“ master”蚂蚁脚本,该脚本调用其他几个蚂蚁脚本。例如: 现在,我拥有用于A,B和C的所有单独版本。通过这个,我的意思是我可以从这些文件夹中的任何一个运行“ ant”,它将构建项目并生成一个jar文件。如果以这种方式构建A,B和C,则我有3个jar文件。然后,我可以将它们