How to extend/add a Bouncycastle digest algorithm for CMS? - java

With Java I want to cryptographically sign given byte[] hash with RSA and return byte[] signature in the Cryptographic Message Syntax (CMS) format.
I am using the Bouncycastle Java API (BC) for this purpose and face the problem of the not existing NONEwithRSA signature type. (java.lang.IllegalArgumentException: Unknown signature type requested: NONEWITHRSA).
I, unfortunately, cannot change the input nor the required output format so I am required to extend/add to the existing BC algorithm so that I can use NONEwithRSA instead of all the other existing (e.g. SHA256withRSA). How do I do this? I have not found any example in the BC documentation.
My desired usage would be similar to this
byte[] doSigningCMS(byte[] data, X509Certificate cert, PrivateKey key) throws Exception {
CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
CMSTypedData content = new CMSProcessableByteArray(data);
ContentSigner signer = new JcaContentSignerBuilder("NONEwithRSA").build(key);
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().build();
signGen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(dcp).build(signer, cert));
return signGen.generate(content, false).getEncoded();
}
What I have tried so far
Adding my own provider for the JCA with "copying" a bunch of BC code and "fake" SHA256withRSA to basically return a NoneDigest instead of SHA256Digest. But this seems to be very wrong.
Adding my own algorithm to BC with ((BouncyCastleProvider) Security.getProvider("BC")).addAlgorithm(ALG_ALIAS, NoneDigest.class.getName()) which does not seem to work (algorithm is not found)
I hope for some advice and guidance to do this (if even possible) "the right way". Thanks.

"NoneWithRSA" is a RSA digital signature without adding the OID of the digest algorithm. The cryptographic provider does not make the digest either. Basically it is a PKCS#1_v15 encryption with the private key.
You can see how it works in this OpenJDK test https://github.com/ddopson/openjdk-test/blob/master/java/security/Signature/NONEwithRSA.java
Since Bouncycastle seems does not support it, I think you can supply your own ContentSigner implementation using the default provider instead of using JcaContentSignerBuilder
It is supposed that the input data is already hashed, so if you are doing a PKCS#1 signature, I think you need to provide the signature algorithm. Looking at the RFC3477 it depends on the hash algorithm used.
A.2.4 RSASSA-PKCS1-v1_5
The object identifier for RSASSA-PKCS1-v1_5 shall be one of the
following. The choice of OID depends on the choice of hash
algorithm: MD2, MD5, SHA-1, SHA-256, SHA-384, or SHA-512.
String sigAlgo = "SHA256WithRSAEncryption"; // "SHA256WithRSAEncryption" for SHA256, "SHA1WithRSAEncryption" for SHA1, etc.
ContentSigner signer = new CustomContentSigner(key, sigAlgo );
public class CustomContentSigner implements ContentSigner {
private AlgorithmIdentifier algorithmIdentifier;
private Signature signature;
private ByteArrayOutputStream outputStream;
public CustomContentSigner(PrivateKey privateKey, String sigAlgo) {
//Utils.throwIfNull(privateKey, sigAlgo);
this.algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(sigAlgo);
try {
this.outputStream = new ByteArrayOutputStream();
this.signature = Signature.getInstance("NONEwithRSA");
this.signature.initSign(privateKey);
} catch (GeneralSecurityException gse) {
throw new IllegalArgumentException(gse.getMessage());
}
}
#Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return algorithmIdentifier;
}
#Override
public OutputStream getOutputStream() {
return outputStream;
}
#Override
public byte[] getSignature() {
try {
signature.update(outputStream.toByteArray());
return signature.sign();
} catch (GeneralSecurityException gse) {
gse.printStackTrace();
return null;
}
}
}
Disclaimer: I do not know if it will work but you can try it

Related

How to validate a RSA SHA1 signature in C++ generated in a java program

We have a java program that signs a xml file with a private key like this:
byte[] privateKeyBytes = ...; // read from the .der file
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory privateKeyFactory = KeyFactory.getInstance("RSA");
PrivateKey = privateKeyFactory.generatePrivate(privateSpec);
Signature rsaSignature = Signature.getInstance("SHA1withRSA");
rsaSignature.initSign(privateKey);
rsaSignature.update(data);
byte[] signature = rsaSignature.sign();
Now i have to validate this signature in C++ using cryptocpp. What i tried:
void Load(const std::string& filename, BufferedTransformation& bt)
{
FileSource file(filename.c_str(), true /*pumpAll*/);
file.TransferTo(bt);
bt.MessageEnd();
}
void LoadKeyFile(const std::string& filename, PublicKey & key)
{
ByteQueue queue;
Load(filename, queue);
key.Load(queue);
}
void verifySignature(std::string xmlString, std::string signature)
{
RSA::PublicKey publicKey;
LoadKeyFile(..., publicKey);
RSASS<PSS, SHA1>::Verifier verifier(publicKey);
// also tried RSASS<PSSR, SHA1>::Verifier verifier(publicKey);
if (!verifier.VerifyMessage((byte*)xmlString.c_str(), xmlString.size(),
(byte*)signature.c_str(), signature.size()) )
{
// always returns false
}
}
I validated that the data passed as xmlString is binary identical with the one used for creating the signature in java. The signature is base64 encoded and i also tried using it decoded.
I always failed so far.
If i create a signature with the private key using cryptopp - then validating the created signature works without problems.
What may be the problem here? I am sure this has already been done?
The solution is
RSASS<PKCS1v15, SHA1>::Verifier verifier(publicKey);
Instead of
RSASS<PSS, SHA1>::Verifier verifier(publicKey);
Thanks a lot to #Topaco!

Create CipherOutputStream with PGP Bouncy Castle dependency

I want to create a OutputStream from another OutputStream in which the new OutputStream will automatically encrypt the content I write to that OutputStream. I want to use Bouncy Castle since I am already using that dependency for other functionality.
I see various questions over the internet how to encrypt data with Bouncy Castle, but the answers either encrypt a given File (I don't use files, I use OutputStreams) or have a huge amount of code I need to copy paste. I can not believe it must be that difficult.
This is my setup:
I am using this Bouncy Castle dependency (V1.68)
I am using Java 8
I have a public and private key generated by https://pgpkeygen.com/. The algorithm is RSA and the keysize is 1024.
I saved the public key and private key as a file on my machine
I want to make sure the test below passes
I have some code commented out, the init function on Cipher (the code compiles, but the test fails). I don't know what I should put in as second argument in the init function.
The read functions are from: https://github.com/jordanbaucke/PGP-Sign-and-Encrypt/blob/472d8932df303d6861ec494a3e942ea268eaf25f/src/SignAndEncrypt.java#L272. Only the testEncryptDecryptWithoutSigning is writting by me.
Code:
#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.");
}
As a preliminary, that website doesn't generate a keypair, but three. Historically in PGP there has long been some ambiguity between actual cryptographic keys and keypairs, and what PGP users call keys, because it is common for a given user (or entity or role etc) to have one 'master' or 'primary' key and one or more subkey(s) tied to that masterkey. For DSA+ElG keys it was technically necessary to use a subkey (and not the masterkey) for encryption; for RSA it is considered good practice to do so because it is often better to manage (e.g. potentially revoke) these keys separately. Some people also consider it good practice to use a subkey rather than the masterkey for signing data, and use the masterkey only for signing keys (which PGP calls certifying - C), but some don't. When PGP users and documents talk about a 'key' they often mean the group of a masterkey and (all) its subkey(s), and they say masterkey or subkey (or encryption subkey or signing subkey) to mean a specific actual key.
When you choose RSA that website generates a masterkey (keypair) with usage SCEA -- i.e. all purposes -- AND TWO subkeys each with usage SEA -- all purposes valid for a subkey. This is nonsensical; if the masterkey supports Signing and Encryption most PGP programs will never use any subkey(s), and even if it didn't or you override it, there is no meaningful distinction between the subkeys and no logical way to choose which to use.
And BouncyCastle exacerbates this by changing the terminology: most PGP programs use key for either an actual key or a group of masterkey plus subkeys as above, and 'public' and 'secret' key to refer to the halves of each key or group, and 'keyring' to refer to all the key group(s) you have stored, typically in a file, which might be for many different people or entities. Bouncy however calls the group of a masterkey with its subkeys (in either public or secret form) a KeyRing, and the file containing possibly multiple groups a KeyRingCollection, both of them in Public and Secret variants. Anyway ...
Your first problem is you have it backwards. In public key cryptography we encrypt with the public key (half) and decrypt with the private key (half) which PGP (and thus BCPG) calls secret. Further, because private/secret keys in PGP are password-encrypted, to use it we must first decrypt it. (The same is true in 'normal' JCA keystores like JKS and PKCS12, but not necessarily in others.)
Your second problem is the types. Although a (specific) PGP key for a given asymmetric algorithm is semantically just a key for that algorithm, plus some metadata (identity, preference, and trust/signature information), the Java objects (classes) in BCPG for PGP keys are not in the type hierarchy of the objects used for keys in Java Crypto Architecture (JCA). In simpler words, org.bouncycastle.openpgp.PGPPublicKey is not a subclass of java.security.PublicKey. So these key objects must be converted to JCA-compatible objects to be used with JCA.
With those changes and some additions, the following code works (FSVO work):
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));
}
(I find it clearer to have the code in one place in execution sequence, but you can break it out into pieces as you had it with no substantive change.)
I kept your logic (from Bouncy examples) of choosing the first encryption-capable public key either master or sub from the first group having one which per above Bouncy miscalls a KeyRing; since per above the website you used gives the masterkey SCEA this is always the masterkey. It isn't possible to similarly select a secret/private key depending on whether it allows encryption, and in any case there is no guarantee that the public key file will always be in the same order, so the correct way to choose the decryption key is to match the keyid from the key that was used for encryption.
Also, modern encryption algorithms (both asymmetric like RSA and symmetric like AES or '3DES') produce data that is arbitrary bit patterns, and in particular mostly NOT valid UTF-8, so 'decoding' those bytes as UTF-8 to compare to the plaintext is generally going to corrupt your data; if you want this (unnecessary) check you should instead compare the byte arrays as I show.
Finally, in case you don't know, asymmetric algorithms are not normally used to encrypt data of large or variable size, which is what you would normally use Java streams for; this is also explained in the wikipedia article. This approach, using RSA PKCS1-v1_5 directly, with a 1024-bit key as you have, can only handle 117 bytes of data (which may be fewer than 117 characters, depending).
And if you expect the result to be compatible or interoperable with any real PGP implementation, it definitely isn't -- which means the effort of converting from PGP key format is wasted, because you could have simply generated JCA-form keys directly in the first place, following the basic tutorials on the Oracle website or hundreds of examples here on Stack. If you want to interoperate with GPG or similar, you need to use the BCPG classes for PGP-format encryption and decryption, which can layer on plain byte streams, but are completely different from and incompatible with JCA's Cipher{Input,Output}Stream.

Creating RSA Public Key From String

I've generated this test public key using 1024 RSA and then encoded it to DER and Base64 in another coding platform. I copied the key into a string in Android/Eclipse and I am trying to turn it into a public key using KeyFactory. It just keeps giving me an InvalidKeySpecException no matter what I try. Any advice at all would be appreciated.
private void prepKeys() {
String AppKeyPub = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5" +
"5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ" +
"OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// create the key factory
try {
KeyFactory kFactory = KeyFactory.getInstance("RSA");
// decode base64 of your key
byte yourKey[] = Base64.decode(AppKeyPub,0);
// generate the public key
X509EncodedKeySpec spec = new X509EncodedKeySpec(yourKey);
PublicKey publicKey = (PublicKey) kFactory.generatePublic(spec);
System.out.println("Public Key: " + publicKey);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The key you have is in PKCS#1 format instead of SubjectPublicKeyInfo structure that Java accepts. PKCS#1 is the encoding of the RSA parameters only and lacks things such as an algorithm identifier. SubjectPublicKeyInfo uses PKCS#1 internally - for RSA public keys anyway.
As the PKCS#1 public key is at the end of the SubjectPublicKeyInfo structure it is possible to simply prefix the bytes so that they become an RSA SubjectPublicKeyInfo. That solution is easier to perform without additional libraries such as Bouncy Castle. So if you need to go without an external library then you may have a look at my answer here.
Alternatively a simple BER decoder could be written to decode the structure into the two BigInteger values. The structure itself is not that complicated but the BER/DER length encoding takes some getting used to.
However, you can also use Bouncy Castle (lightweight API) to solve your issues:
String publicKeyB64 = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5"
+ "5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ"
+ "OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// ok, you may need to use the Base64 decoder of bouncy or Android instead
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey generatedPublic = kf.generatePublic(keySpec);
System.out.printf("Modulus: %X%n", modulus);
System.out.printf("Public exponent: %d ... 17? Why?%n", publicExponent); // 17? OK.
System.out.printf("See, Java class result: %s, is RSAPublicKey: %b%n", generatedPublic.getClass().getName(), generatedPublic instanceof RSAPublicKey);
As you can see it actually only requires a single class as interface, although that is of course backed up with the entire ASN.1/BER decoder functionality within Bouncy Castle.
Note that it may be required to change the Base 64 decoder to the Android specific one (android.util.Base64). This code was tested on an equivalent Java runtime.
For those who dont want to use Bouncy Castle
public class RSAKeySeperation {
public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException {
String publicKeyB64 = "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBV8xakN/wOsB6qHpyMigk/5PrSxxd6tKTJsyMIq5f9npzZue0mI4H2o8toYImtRk6VHhcldo0t7UwsQXmFMk7D"
+ "i3C53Xwfk7yEFSkXGpdtp/7fbqNnjVoJl/EPcgoDsTPrHYF/HgtmbhzuYvYeY1zpV0d2uYpFxAuqkE9FreuuH0iI8xODFe5NzRevXH116elwdCGINeAecHKgiWe"
+ "bGpRPml0lagrfi0qoQvNScmi/WIN2nFcI3sQFCq3HNYDBKDhO0AEKPB2FjvoEheJJwTs5URCYsJglYyxEUon3w6KuhVa+hzYJUAgNTCsrAhQCUlX4+5LOGlwI5gonm1DYvJJZAgMBAAEB";
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey generatePublic = (RSAPublicKey) kf.generatePublic(spec);
BigInteger modulus = generatePublic.getModulus();
System.out.println(modulus);
BigInteger exponent = generatePublic.getPublicExponent();
System.out.println(exponent);
}
}

Store PGP (public) keys in java keystore - Bouncycastle

I am using bouncycastle (JAVA) for signing, encryption, decryption and signatures' verification in implementation of SSO.
I have raw PGP public and private keys and I need to store them in Java keystore.
These PGP public keys have no certificate.
I understand that for public keys (according to javadoc of Keystore: http://docs.oracle.com/javase/6/docs/api/java/security/KeyStore.html) I have to create certificate. Once certificate is created I can import it to the keystore as KeyStore.TrustedCertificateEntry.
However, I am not able to create certificate entry for type org.bouncycastle.openpgp.PGPPublicKey.
I have searched through the web but could not find any valid example:
Bouncycastle documentation: http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation
Generates certificate for X.509 keys -
Bouncycastle examples - org.bouncycastle.openpgp.examples.DirectKeySignature:
Add certificat (object of type PGPSignature) directly to the PGPPublicKey.
To conclude - I have signed (certified) PGPPublicKey but I am not able to store this type of Key into the java keystore.
OutputStream out = new ByteArrayOutputStream();
if (armor)
{
out = new ArmoredOutputStream(out);
}
PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(secretKeyPass.toCharArray(), "BC");
PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC");
sGen.initSign(PGPSignature.DIRECT_KEY, pgpPrivKey);
BCPGOutputStream bOut = new BCPGOutputStream(out);
sGen.generateOnePassVersion(false).encode(bOut);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
boolean isHumanReadable = true;
spGen.setNotationData(true, isHumanReadable, notationName, notationValue);
PGPSignatureSubpacketVector packetVector = spGen.generate();
sGen.setHashedSubpackets(packetVector);
bOut.flush();
return PGPPublicKey.addCertification(keyToBeSigned, sGen.generate()).getEncoded();
I am mainly interested in programatic solution (java source code) but examples that use some tools will be helpful too.
Thanks!
I think you should extract a java.security.PublicKey from your PGPPublicKey and use that to construct an X509Certificate which can be stored in a keystore.
JcaPGPKeyConverter c = new JcaPGPKeyConverter();
PublicKey publicKey = c.getPublicKey(pgpPublicKey);
// ... Use Bouncy's X509V3CertificateGenerator or X509v3CertificateBuilder
// ... to construct a self-signed cert
X509Certificate x509Certificate = // ...
// ... add cert to KeyStore
To create an X509Certificate from a PublicKey see: Generate random certificates.
If you only want to save the public key, why cannot you just save the key content into Java keystore? Then retrieve the content and convert into a PGPPublicKey object when you need it.
Create a wrapper class first
public class PgpPublicKeyWrapper implements Key {
private final String keyContent;
public PgpPublicKeyWrapper(final String keyContent) {
this.keyContent = keyContent;
}
#Override
public String getAlgorithm() {
return "PGP-PublicKey"; // you can call whatever you want
}
#Override
public String getFormat() {
return "RAW"; // has to be raw format
}
#Override
public byte[] getEncoded() {
return keyContent.getBytes();
}
}
Then you can do this to save it
keyStore.setKeyEntry("think a name for alias", new PgpPublicKeyWrapper(key), PASSWORD, null);
When you want to retrieve it
Key key = this.keyStore.getKey(alias, PASSWORD);
InputStream is = new ByteArrayInputStream(key.getEncoded());
PGPPublicKey publicKey = readPublicKey(is);
For readPublicKey(), you can find a lot of examples online about how to read InputStream to a PGPPublicKey object.

Java, Digital Signature with BouncyCastle

In Java, I tried to sign a byte[] (which is my sha256 digest of my document) with bouncy castle and a certificate in this specification:
http://www.ebics.org/fileadmin/unsecured/specification/spec_current_EN/EBICS_Specification_2.5_final-16-05-2011.pdf
in chapter 14.1.4.1.1 Digital signature generation.
I found in bouncy's java doc this method:
public static byte[] signer(byte[] datas, Certificat cert) {
try {
List<X509Certificate> certList = new ArrayList<X509Certificate>();
CMSTypedData msg = new CMSProcessableByteArray(datas);
certList.add(cert.getCertificat());
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha256signer = new JcaContentSignerBuilder(
"SHA256withRSA").setProvider("BC").build(
cert.getPrivateKey());
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC")
.build()).build(sha256signer, cert.getCertificat()));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
return sigData.getEncoded();
}
catch (Exception e) {
throw new RuntimeException(
"Erreur lors de la signature du document", e);
}
I don't know if this signature is really in accordance with PKCS#1 1.5 required by the specification. Do I have to add the padding manually? And the OID for RSA256?
EBICS signature A005 is a RSA signature with SHA-256 digest algorithm and PKCS#1 1.5 padding. However the code sample you pasted here is creating a CMS signature which uses a "low level" RSA signature but is a much more complex structure (for comprehensive details, see RFC 5652 http://www.rfc-editor.org/rfc/rfc5652.txt).
Hopefully, generating the signature you are trying to get is really simple with the java crypto API:
public static byte[] signer(byte[] data, PrivateKey key) {
Signature signer = Signature.getInstance("SHA256WithRSA", "BC");
signer.initSign(key);
signer.update(data);
return signer.sign();
}

Categories

Resources