Retrieve encapsulated content from Bouncy Castle's CMSAuthenticatedData [Java] - java

I'm using the CMSAuthenticatedData from the Bouncy Castle library, version 146. I can create it correctly, verified by inspecting the ASN.1 dump, but I can't seem to retrieve the content.
The authenticated data object is created with a generator, like this:
CMSAuthenticatedDataGenerator generator = new CMSAuthenticatedDataGenerator();
CMSProcessableByteArray myContent = new CMSProcessableByteArray(myBytes);
CMSAuthenticatedData cmsData = generator.generate(myContent, myMacCalculator);
cmsData is then serialized with .getEncoded(), sent to the server, and deserialized with the byte[] constructor. From the RFC, I see myBytes should be in the encapContentInfo field inside the AuthenticatedData object, but I can't get it. Here's what I've tried so far:
// The original byte array I want to retrieve is 633 bytes long.
cmsData.getEncoded();
// returns ASN.1 for authData OID and AuthenticatedData object. Size: 724
cmsData.getContentInfo();
// Same as cmsData.getEncoded(). Size: 724
cmsData.getContentInfo().getContent().getDERObject().getEncoded();
// returns ASN.1 for AuthenticatedData object. Size: 703
So, is this even possible, or should I roll out my own implementation, parsing the ASN.1 byte array?

After weeks of research, I found a solution using org.bouncycastle.asn1.cms.AuthenticatedData:
CMSAuthenticatedData cmsData; // <- this is the object created.
byte[] cmsBytes = cmsData.getContentInfo().getContent().getDERObject().getEncoded();
ASN1Sequence byteSeq = (ASN1Sequence) ASN1Object.fromByteArray(rawAuthenticatedDataBytes)
AuthenticatedData authData = new AuthenticatedData(byteSeq);
byte[] contentBytes = authData.getEncapsulatedContentInfo().getContent().getDERObject().getEncoded()
I sure hope this is not the simplest way, but it was the only one I found.

Related

Equivalent Java function Base64.encode(byte, 0) in Swift

I have the following function in Java(write inside an Android app)
Bitmap bm = BitmapFactory.decodeFile(stringPath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); // bm is the bitmap object era quality 100
byte[] byteData = baos.toByteArray();
byte[] newB = Base64.encode(byteData,0);
It get an Image file and convert it to a byte array.
I need that to work on Swift.
I was able to convert the byteData to Int8, the equivalent of byte array in Swift.
I use the code below:
let filename = "RES4010110001"
let test_image:UIImage = UIImage(named: filename)!
let dataImage = UIImageJPEGRepresentation(test_image, 1)! as Data
var bytes: [Int8] = dataImage.map{Int8(bitPattern: $0)}
When I print this data using print(bytes) I got the same results on iOS and Android when I compare the "byteData" from Android and "bytes" from iOS
But I don't know what is the Java equivalent function below on Swift
Base64.encode(byteData,0);
How can I create an equivalent function on Swift of the function above?
For me, it appears that this function is encoding a byte array. But I have no idea of how I can encode a Int8 Array.
I said Int8 because for me, Int8 is the Swift equivalent of byte type in Java.
edit: I want to encode a Int8 array, I guess this is what the Java function (the function in the post title) is doing inside the Android app.
You can use base64EncodedData to encode your Data to get an encoded Data, rather than working with [Int8].
The Java code passes 0 (DEFAULT) as the flags. According to the docs, this means it's compliant with RFC 2045. RFC 2045 says that lines should be no more than 76 characters, so we should pass lineLength76Characters. RFC 2045 also requires CRLF as line endings, but base64EncodedData seems to insert them automatically even if we don't pass endLineWithCarriageReturn and endLineWithLineFeed.
let filename = "RES4010110001"
if let testImage = UIImage(named: filename),
let dataImage = testImage.jpegData(compressionQuality: 1) {
let encodedData = dataImage.base64EncodedData(options: [.lineLength76Characters])
} else {
// failed to get the UIImage, or the JPEG data, handle the error here...
}
I suggest that you do not work with [Int8] here. The byte[]s in the Java code are clearly not just "lists of numbers between -128 and 127". They represent data buffers, and those are represented by the Data struct in Swift. Don't worry, Both Data and [Int8] share a very similar set of protocol conformances, like MutableCollection, RangeReplaceableCollection, RandomAccessCollection etc. You can do almost everything you can do to an Int8 array, to a Data.
I was able to find an definite answer using part of the answer created by
#Sweeper and from another answer published here on Stackoverflow
let filename = "RES4010110001"
let test_image:UIImage = UIImage(named: filename)!
let dataImage = UIImageJPEGRepresentation(test_image, 1)! as Data // use apenas este que é correto, o galvez usa jpg no android
let dataImageStringBase64:String = dataImage.base64EncodedString(options: [.lineLength76Characters])
let byteArray = [UInt8](dataImageStringBase64.utf8)
The answer posted by #Sweeper just miss this line
let byteArray = [UInt8](dataImageStringBase64.utf8)
This last line of code made the code works perfectly for me.

Data signed in C#, verifies in C# but not in Java [duplicate]

I'm attempting to generate an RSA SHA512 signature on my server application (written in C#) and verify that signature on a client application (written in Java). Prior to the signature exchange, the server generates a public- and private-key pair and shares the public key with the client app by giving it the modulus and exponent values that were generated. The client app stores the values for later, when the signature needs to be verified.
The server is generating a 128-byte modulus and a 128-byte signature. I'm just using a byte array of [0x00, 0x01, 0x02, 0x03] as my source data for the signature while I'm testing.
For some reason, my Java application always fails when validating the signature.
My C# code (creates the signature):
RSACryptoServiceProvider crypto = new RSACryptoServiceProvider();
crypto.FromXmlString(this.myStoredPrivateKeyXml);
List<byte> signatureBytes = new List<byte>();
signatureBytes.Add(0x00);
signatureBytes.Add(0x01);
signatureBytes.Add(0x02);
signatureBytes.Add(0x03);
byte[] signature = crypto.SignData(signatureBytes.ToArray(), "SHA512");
My Java code (that receives/validates the signature):
byte[] expectedData = new byte[4];
expectedData[0] = 0;
expectedData[1] = 1;
expectedData[2] = 2;
expectedData[3] = 3;
byte[] exponent = getStoredExponent(); // three-byte value from server
byte[] modulus = getStoredModulus(); // 128-byte value from server
RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(spec);
Signature verifier = Signature.getInstance("SHA512withRSA");
verifier.initVerify(publicKey);
verifier.update(expectedData);
if(verifier.verify(signature)) {
System.out.println("signature verified");
} else {
System.out.println("signature failed verification"); // always ends up here :(
}
The crypto objects on the C# side are from the System.Security.Cryptography namespace and java.security on the Java side.
The bytes are passed between the apps via web services and base64 strings. I've gone through and printed the values of the byte arrays themselves to make sure the values are correct for the exponent, modulus, and signature between the two applications. Is there any reason why the signature wouldn't be compatible between the two languages/apps? Is there a parameter that I'm missing, maybe? I made them both RSA with SHA512, but perhaps other aspects of the signature need to be accounted for as well?
In the XML representation on the C# side, the data are stored as unsigned big endian (and Base64 encoded).
However, the Java BigInteger(byte[] val)-constructor expects the data as signed (two's complement) big endian.
Therefore the BigInteger(int signum, byte[] magnitude)-constructor must be used, which expects the sign in the first parameter and the data in the second parameter as unsigned big endian, i.e. the following change is necessary:
RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(1, modulus), new BigInteger(1, exponent));

Authenticating signature created on c# by public key on java

I am new to verification and certificates etc ..
I am facing an issue , that I need to sign a message on c# then verify the signature on java , the issue I ma facing that I am unable to load the public key on java on a (PublicKey) object using the Base64 string generated on c# , I used the following code to generate the private and public key on c# side
CspParameters cspParams = new CspParameters { ProviderType = 1 };
cspParams.KeyContainerName = "MyKeyContainer";
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
System.Diagnostics.Debug.WriteLine("pri:" + privateKey);
Console.WriteLine("Key added to container: \n {0}", rsaProvider.ToXmlString(true));
then I used the following code to create a public key on Java side :
X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
KeyFactory xx = KeyFactory .getInstance("RSA");
PublicKey ssx= xx.generatePublic(specc);
note that I copied the base64 public key string from the c# console .
When I try to run the code on java side the I get the following exception :
java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)
I need to find a way to generate private and public key on c# (and generate a .cer file for the public key) to load it on java side , or find a way to load the base64 public key string into a (Publickey) object on java side . please help !
Option 1: Same data, different format.
The easiest way to transmit a public RSA key from .NET is to check that the public exponent value is { 01 00 01 } and then send the modulus value. On the receiver side you accept the modulus and assert the public exponent.
RSAParameters keyParams = rsa.ExportParameters(false);
if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
throw new InvalidOperationException();
Send(keyParams.Modulus);
Then Creating RSA keys from known parameters in Java says you can straightforwardly recover it on the Java side.
Option 2: Same format, different parser.
The next option you have is to keep using the CSP blob, but writing a parser in Java. The data is the result of calling CryptExportKey with PUBLICKEYBLOB, making your data layout as described at https://msdn.microsoft.com/en-us/library/ee442238.aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB.
In summary:
A header (which you could decide to skip, or just test it for equal to the fixed value(s) that you expect):
A byte, value 0x06 (PUBLICKEYBLOB)
A byte, value 0x02 (blob v2)
A short, value 0x0000 (reserved)
An integer (stored as little-endian) identifying the key as RSA (0x0000A400 or 0x00002400)
An integer (stored as little-endian) identifying the next segment as an RSA public key (a bit redundant, but technically a different structure now): 0x31415352
After all that comes the relevant data:
The bit-length of the modulus stored as a little-endian unsigned integer. For your 1024-bit example this will be 1024, aka 0x00000400, aka { 00 04 00 00 }(LE).
The public exponent, stored as a little-endian unsigned integer. This is almost always 0x00010001 (aka { 01 00 01 00 }), but since it's there you should respect it.
The next bitLen/8 bytes represent the modulus value. Since this is the public key that should be "the rest of the bytes in this array".
Option 3: Build a certificate
.NET Framework doesn't have this capability built-in (as of the current version, 4.7). You can P/Invoke to CertCreateSelfSignCertificate, but that would involve quite a lot of change (since RSACryptoServiceProvider won't let you get at the key handle, so you'll have to P/Invoke all of that, too).
You could "bit bang" out the DER-encoded certificate yourself. While fun, this is hard to get right, and probably not a viable path.
If you can move to .NET Core, the ability to create certificates has been added to .NET Core 2.0 via the CertificateRequest class. For a very simple certificate from your key:
var certReq = new CertificateRequest(
"CN=SubjectCN",
rsaProvider,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// add any extensions you want. I'm not adding any because I said "simple".
DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;

receiving asn.1 message via java socket

I am using BinaryNotes to encode and decode my ASN.1 messages and I have classes that were compiled using the bnotes compiler. I am using the encoding/decoding example contained in the pdf here:
http://bnotes.sourceforge.net/BinaryNotes.pdf page 11
here is the example code:
// Encoding for Java
TestSequence sequence = new TestSequence();
sequence.setField1(10L);
sequence.setField3("Hello");
// Inner class for implicitly ASN.1 type declaration
TestSequence.Field4ChoiceType choice = sequence.new Field4ChoiceType();
choice.selectField2(0.5);
sequence.setField4(choice);
IEncoder< TestSequence> encoder=CoderFactory.getInstance).newEncoder("BER");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
encoder.encode(sequence, outputStream);`
Now I have an input stream that contains my encoded data but in order to decode I need to know the class of the sequence which in the example case is "TestSequence".
// Decoding for Java
IDecoder decoder = CoderFactory.getInstance().newDecoder("BER");
// Decoding the specified input stream
TestSequence seq = decoder.decode(stream, TestSequence.class);
System.out.println(seq.getField1());
if(seq.isField3Present())
System.out.println(seq.getField3());
If I receive the data as a ByteArrayInputStream how do I determine which classname to pass to the decoder.decode method?

Timestamp request with file hash already generated in client

I need to make a timpestamp request to a tsa of a large data file and so i am generating hash in client using javscript crypto-js.
The problem comes when later in java i try to make the request. Apparently the method TimeStampRequestGenerator.generate needs a byte[] parameter that in examples i can se that is a MessageDigest object generated from the content of the file and i can't find the way to use only the hash already generated.
Is it possible to make a request using only the hash of the file already generated ?
Thanks
After hard testing, i have found the solution.
The SHA-256 hash generated in javascript can be used directly in bouncyclaste after some type conversion as follows:
byte[] decodedHex = Hex.decodeHex(digest.toCharArray());
so you can use it as a normal
java.security.MessageDigest
when they are both converted to
byte[]
full code here:
// Get hash code as hexadecimal string (generated by crypto-js)
String digest = servletRequest.getParameter("digest");
// hexadecimal to string decoder
byte[] decodedHex = Hex.decodeHex(digest.toCharArray());
// Timestamp request
TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator();
TimeStampRequest req = reqgen.generate(TSPAlgorithms.SHA256, decodedHex);
byte request[] = req.getEncoded();
...

Categories

Resources