AES encryption under contiki on CC2650 - aes

I am currently trying to add AES encryption to an existing beacon-demo that uses the TI cc2650 sensortag. I am using the AES API provided by contiki under core/lib.
My main looks like this:
static const uint8_t AES_key[16] = { 0xC0 , 0xC1 , 0xC2 , 0xC3 ,
0xC4 , 0xC5 , 0xC6 , 0xC7 ,
0xC8 , 0xC9 , 0xCA , 0xCB ,
0xCC , 0xCD , 0xCE , 0xCF };// AES Key
static uint8_t plain_text[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16}; // Plain-text to be encrypted.
const struct aes_128_driver AES_128;
.
.
.
printf("Plain_Text: %d \r\n", plain_text);
AES_128.set_key(AES_key);
AES_128.encrypt(plain_text);
printf("Encrypted_Text: %p\r\n", plain_text);
Unfortunately when I run the code the plain text is unchangeable. Using some extra prints, I realize that the encrypt function is working but the output is still unchangeable. Can someone please tell me what am I doing wrong here?
Please note that I already added the following line to my conf file:
#define AES_128_CONF aes_128_driver

Well as #kfx pointed out in the comments const struct aes_128_driver AES_128 was shadowing the global variable.

Related

Trinamic TMC2209 STM32 UART not reading from device

I've been struggling for ages, finally caved and asking for help now.
There are very little resources on STM32 for this driver that I have seen.
I can use the device with the STEP/DIR interface but I want to be able to do the more advanced things now with only the single UART line and not the STEP/DIR lines.
I am trying to control a TMC2209 in the most basic form to understand how it all works, without libraries and such.
I want to get basic controls working so I can understand it and build up my own code from there, right now the goal is to just talk to the thing and get it to spin.
I am using a STM32F103, I have setup the USART3 line (PC10) in signal wire half duplex mode and using the following code to try read from the CHOPCONF register.
Sync = 0x05; // sync byte to start the Tx
Address = 0x00; // MS1 and MS2 are pulled low so the device address is 0
RegAddress = 0x6C; // Trying for a basic read from the CHOPCONF Reg
motor_CRC = 0xCA; // CRC that has been worked out
HAL_UART_Transmit(&huart3,&Sync,sizeof(Sync),20);
HAL_UART_Transmit(&huart3,&Address,sizeof(Address),20);
HAL_UART_Transmit(&huart3,&RegAddress,sizeof(RegAddress),20);
HAL_UART_Transmit(&huart3,&motor_CRC,sizeof(motor_CRC),20);
// Receive the data from the TMC2209
HAL_UART_Receive(&huart3, &ReplySync, sizeof(ReplySync), 200);
HAL_UART_Receive(&huart3, &ReplyMasterAddr, sizeof(ReplyMaster), 20);
HAL_UART_Receive(&huart3, &ReplyAddr, sizeof(ReplyAddr), 20);
HAL_UART_Receive(&huart3, &Reply1, sizeof(Reply1), 20);
HAL_UART_Receive(&huart3, &Reply2, sizeof(Reply2), 20);
HAL_UART_Receive(&huart3, &Reply3, sizeof(Reply3), 20);
HAL_UART_Receive(&huart3, &Reply4, sizeof(Reply4), 20);
HAL_UART_Receive(&huart3, &ReplyCRC, sizeof(ReplyCRC), 20);
HAL_Delay(1000);
I expect the following : ( according to the Datasheet that is,,, )
ReplySync >>> 0x05
ReplyMasterAddr>>> 0xFF
ReplyAddr >>> 0xCA
Reply1 >>> 0x53
Reply2 >>> 0x00
Reply3 >>> 0x00
Reply4 >>> 0x10
ReplyCRC >>> I dont know this one yet,,,,
But I am getting this :
ReplySync >>> 0x05
ReplyMasterAddr>>> 0x00
ReplyAddr >>> 0x5D
Reply1 >>> 0x09
Reply2 >>> 0x08
Reply3 >>> 0x00
Reply4 >>> 0x02
ReplyCRC >>> 0x09
I am not sure if I am transmitting these in the correct order or maybe the HAL function should not be used for this at all ?
Any help or pointers in the right direction would be greatly appreciated.

WebCrypto AES-CBC outputting 256bit instead of 128bits

I'm playing with WebCrypto and I'm getting a confusing output.
The following test case encrypts a random 16byte (128bit) plain text with a newly generated 128bit key and 128bit random IV but is outputting a 32byte (256bit) output.
If I remember the details of AES-CBC it should output 128bit blocks.
function test() {
var data = new Uint8Array(16);
window.crypto.getRandomValues(data);
console.log(data)
window.crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 128,
},
false,
["encrypt", "decrypt"]
)
.then(function(key){
//returns a key object
console.log(key);
window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: window.crypto.getRandomValues(new Uint8Array(16)),
},
key,
data
)
.then(function(encrypted){
console.log(new Uint8Array(encrypted));
})
.catch(function(err){
console.error(err);
});
})
.catch(function(err){
console.error(err);
});
}
Example output:
Uint8Array(16) [146, 207, 22, 56, 56, 151, 125, 174, 137, 69, 133, 36, 218, 114, 143, 174]
CryptoKey {
algorithm: {name: "AES-CBC", length: 128}
extractable: false
type: "secret"
usages: (2) ["encrypt", "decrypt"]
__proto__: CryptoKey
Uint8Array(32) [81, 218, 52, 158, 115, 105, 57, 230, 45, 253, 153, 54, 183, 19, 137, 240, 183, 229, 241, 75, 182, 19, 237, 8, 238, 5, 108, 107, 123, 84, 230, 209]
Any idea what I've got wrong.
(Open to moving to crypto.stackexchange.com if more suitable)
I'm testing on Chrome 71 on MacOS at the moment.
Yes. The extra 16 bytes is the padding. Even when the message text is a multiple of the block size, padding is added, otherwise the decryption logic doesn't know when to look for padding.
The Web Cryptography API Specification says:
When operating in CBC mode, messages that are not exact multiples of
the AES block size (16 bytes) can be padded under a variety of padding
schemes. In the Web Crypto API, the only padding mode that is
supported is that of PKCS#7, as described by Section 10.3, step 2, of
[RFC2315].
This means unlike other language implementations (like Java) where you can specify NoPadding when you know that your input message text is always going to be a multiple of block size (128 bits for AES), Web Cryptography API forces you to have PKCS#7 padding.
If we look into RFC2315:
Some content-encryption algorithms assume the input length is a
multiple of k octets, where k > 1, and let the application define a
method for handling inputs whose lengths are not a multiple of k
octets. For such algorithms, the method shall be to pad the input at
the trailing end with k - (l mod k) octets all having value k - (l mod
k), where l is the length of the input. In other words, the input is
padded at the trailing end with one of the following strings:
01 -- if l mod k = k-1
02 02 -- if l mod k = k-2
.
.
.
k k ... k k -- if l mod k = 0
The padding can be removed unambiguously since all input is padded and
no padding string is a suffix of another. This padding method is
well-defined if and only if k < 256; methods for larger k are an open
issue for further study.
Note: k k ... k k -- if l mod k = 0
If you refer to the subtle.encrypt signature, you have no way to specify the padding mode. This means, the decryption logic always expects the padding.
However, in your case, if you use the Web Cryptography API only for encryption and your Python app (with NoPadding) only for decryption, I think you can simply strip off the last 16 bytes from the cipher text before feeding it to the Python app. Here is the code sample just for demonstration purpose:
function test() {
let plaintext = 'GoodWorkGoodWork';
let encoder = new TextEncoder('utf8');
let dataBytes = encoder.encode(plaintext);
window.crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 128,
},
true,
["encrypt", "decrypt"]
)
.then(function(key){
crypto.subtle.exportKey('raw', key)
.then(function(expKey) {
console.log('Key = ' + btoa(String.
fromCharCode(...new Uint8Array(expKey))));
});
let iv = new Uint8Array(16);
window.crypto.getRandomValues(iv);
let ivb64 = btoa(String.fromCharCode(...new Uint8Array(iv)));
console.log('IV = ' + ivb64);
window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: iv,
},
key,
dataBytes
)
.then(function(encrypted){
console.log('Cipher text = ' +
btoa(String.fromCharCode(...new Uint8Array(encrypted))));
})
.catch(function(err){
console.error(err);
});
})
.catch(function(err){
console.error(err);
});
}
The output of the above is:
IV = qW2lanfRo2H/3aSLzxIecA==
Key = 0LDBq5iz243HBTUE/lrM+A==
Cipher text = Wa4nIF0tt4PEBUChiH1KCkSOg6L2daoYdboEEf+Oh6U=
Now, I use take these as input, strip off the last 16 bytes of the cipher text and still get the same message text after decryption using the following Java code:
package com.sapbasu.javastudy;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptCBC {
public static void main(String[] arg) throws Exception {
SecretKey key = new SecretKeySpec(Base64.getDecoder().decode(
"0LDBq5iz243HBTUE/lrM+A=="),
"AES");
IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(
"qW2lanfRo2H/3aSLzxIecA=="));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] cipherTextWoPadding = new byte[16];
System.arraycopy(Base64.getDecoder().decode(
"Wa4nIF0tt4PEBUChiH1KCkSOg6L2daoYdboEEf+Oh6U="),
0, cipherTextWoPadding, 0, 16);
byte[] decryptedMessage = cipher.doFinal(cipherTextWoPadding);
System.out.println(new String(decryptedMessage, StandardCharsets.UTF_8));
}
}

CryptoSwift with AES128 CTR Mode - Buggy counter increment?

i've encountered a problem on the CryptoSwift-API (krzyzanowskim) while using AES128 with the CTR-Mode and my test function (nullArrayBugTest()) that produces on specific counter values (between 0 and 25 = on 13 and 24) a wrong array count that should usually be 16!
Even if I use the manually incremented "iv_13" with the buggy value 13 instead of the default "iv_0" and the counter 13...
Test it out to get an idea what I mean.
func nullArrayBugTest() {
var ctr:CTR
let nilArrayToEncrypt = Data(hex: "00000000000000000000000000000000")
let key_ = Data(hex: "000a0b0c0d0e0f010203040506070809")
let iv_0: Array<UInt8> = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]
//let iv_13: Array<UInt8> = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x1c]
var decryptedNilArray = [UInt8]()
for i in 0...25 {
ctr = CTR(iv: iv_0, counter: i)
do {
let aes = try AES(key: key_.bytes, blockMode: ctr)
decryptedNilArray = try aes.decrypt([UInt8](nilArrayToEncrypt))
print("AES_testcase_\(i) for ctr: \(ctr) withArrayCount: \(decryptedNilArray.count)")
}catch {
print("De-/En-CryptData failed with: \(error)")
}
}
}
Output with buggy values
The question why I always need the encrypted array with 16 values is not important :D.
Does anybody know why the aes.decrypt()-function handles that like I received?
Thanks for your time.
Michael S.
CryptoSwift defaults to PKCS#7 padding. Your resulting plaintexts have invalid padding. CryptoSwift ignores padding errors, which IMO is a bug, but that's how it's implemented. (All the counters that you're considering "correct" should really have failed to decrypt at all.) (I spoke this over with Marcin and he reminded me that even at this low level, it's normal to ignore padding errors to avoid padding oracle attacks. I'd forgotten that I do it this way too....)
That said, sometimes the padding will be "close enough" that CryptoSwift will try to remove padding bytes. It usually won't be valid padding, but it'll be close enough for CrypoSwift's test.
As an example, your first counter creates the following padded plaintext:
[233, 222, 112, 79, 186, 18, 139, 53, 208, 61, 91, 0, 120, 247, 187, 254]
254 > 16, so CryptoSwift doesn't try to remove padding.
For a counter of 13, the following padded plaintext is returned:
[160, 140, 187, 255, 90, 209, 124, 158, 19, 169, 164, 110, 157, 245, 108, 12]
12 < 16, so CryptoSwift removes 12 bytes, leaving 4. (This is not how PKCS#7 padding works, but it's how CryptoSwift works.)
The underlying problem is you're not decrypting something you encrypted. You're just running a static block through the decryption scheme.
If you don't want padding, you can request that:
let aes = try AES(key: key_.bytes, blockMode: ctr, padding: .noPadding)
This will return you what you're expecting.
Just in case there's any confusion by other readers: this use of CTR is wildly insecure and no part of it should be copied. I'm assuming that the actual encryption code doesn't work anything like this.
I guess the encryption happens without the padding applied, but then u use padding to decrypt. To fix that, use the same technique on both sides. That said, this is a solution (#rob-napier answer is more detailed):
try AES(key: key_.bytes, blockMode: ctr, padding: .noPadding)

Generating const arrays in C from a database

I have to provide some C++ code consisting of const arrays initialised with well-known values.
More precisely, several arrays containing
Between 0 and 255 strings for a text-based user interface
Metadata, like lengths and positions of those strings
Data representing a hierarchical menu structure
Metadata connecting the menu structure with the strings and configuration data stored elsewhere
The menu items are accessed by their index in an array, and are related to other linked menu items, also accessed by index.
Here is the code I want to generate automatically from a better editable document:
/* gl_PsNMenuType holds one byte of metadata for each menu. This byte will describe
whether the menu only refers to a bunch of submenus or if it is a terminal item, where
the user actually can enter a setting to be saved */
const uint8_t gl_PsNMenuType[MAXMENUS] PROGMEM = { 0, 0, 0, 0, 0};
/* gl_PsNMenuParent holds the index of each menu's parent menu. i.e. tree one step up */
const uint8_t gl_PsNMenuParent[MAXMENUS] PROGMEM = { 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; // referenz auf den übergeordneten MP
/* gl_PsNMenuFixTexts is a list of identifiers text snippets which build the layout of
the respective menu. The text snippets are stored in gl_PsFixTexts and the respective
index of a text snippet is saved here. MFTS is a list sentinel*/
const uint8_t gl_PsNMenuFixTexts[MAXMENUS][MAXFIXTEXTSPM] PROGMEM = {
{ 20, 2, MFTS}, // text snippets for menu 0
{2, 9, 1, MFTS}, // for menu 1
{2, 10, MFTS}, //2
{2, 11, MFTS}, //3
{2, 13, MFTS} //4
};
/* gl_PsNMenuParentListText holds the identifiers of the text snippets which shall be
displayed by the parent menu while selection before descending to the submenus. There's
one such snippet for each menu. */
const uint8_t gl_PsNMenuParentListText[MAXMENUS] PROGMEM = { 0, 9, 10, 11, 13};
/* gl_PsNMenuFtPos closely relates to gl_PsNMenuFixTexts. The positions on the display
for each text snippet of a menu are stored here. */
const uint8_t gl_PsNMenuFtPos[MAXMENUS][MAXFIXTEXTSPM] PROGMEM = { // Liste der Fixtextpositionen
{ 0, 5, MFTS, MFTS, MFTS}, // 0
{ 0, 5, MFTS, MFTS, MFTS}, // 1
{ 0, 5, MFTS, MFTS, MFTS}, // 2
{ 0, 5, MFTS, MFTS, MFTS}, // 3
{ 0, 5, MFTS, MFTS, MFTS}, // 4
};
/* gl_PsNMenuChildren stores the indices of all submenus reachable from a certain menu
this is essentially the structure of the menu tree MAXMENUS is used for array size and
also as sentinel*/
const uint8_t gl_PsNMenuChildren[MAXMENUS][MAXMENUCHILDREN] PROGMEM = {
{ 5, 1, 2, 3, 4, MAXMENUS}, // 0
{ 4, 7, 8, 9, MAXMENUS}, // 1
{1, MAXMENUS}, //2
{1, MAXMENUS}, // 3
{3, 5, 6, MAXMENUS} // 4
};
/* finally the text snippets */
const uint8_t gl_PsFixTexts[MAXFIXTEXTS][MAXFIXTEXTLEN] PROGMEM = {
{5, 0x54, 0x65, 0x73, 0x74, 0x31 }, // 0: Test1
{9, 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x62 }, // 1: bla
{4, 0x4d, 0x65, 0x6e, 0x75}, // 2: Menu
{4, 0x4d, 0x61, 0x69, 0x6e}, // 20: Main
{4, 0x65, 0x78, 0x69, 0x74}, // 21: exit
{4, 0x62, 0x61, 0x63, 0x6b} // 22: back
};
This is shortened for brevity and dysfunctional in this state.
As the contents of those arrays are exclusively uint8_t characters it would be much simpler to write the data automatically, while ensuring that the referencing indices are all correct.
I want some other data describing the menu structure in a more elaborate but maintainable way.
I thought about making a spreadsheet where I could show the hierarchy across the columns and enter all the data. Then some tool would be needed to break down the hierarchy.
I hope someone can let me know of any tools that are suitable for this task. If there's nothing available then I have to write a Perl conversion program. Are there any useful CPAN libraries?
Edit: some further explanation of the use of the arrays
This data will be utilised by an user interface function, which is rather simplicistic. There will be an identifier pointing to the actual menu entry, which is edited or viewed by the user. This identifier will be used as index wherever MAXMENUS is defining the array size. these arrays describe the look and feel of every menu entry. The menu entries refer to different strings (gl_PsFixTexts) and positions (gl_PsNMenuFtPos) which declare, how and where to display strings when processing a certain menu entry.
The most important arrays are gl_PsNMenuParent and gl_PsNMenuChildren which point to the menu entries above and below in hierarchy. This is, how the code will let the user navigate through the menu structure.
There's also some information if this is an terminal entry in the menu tree, i.e. if there's some data to change, which again means modifying a setting for the software.
Here is my best answer, based on what you have told us.
Menu information like that is commonly stored as XML. An XML data file would allow you to relate menu items to their text labels and their children quite expressively.
In the end you could write a GUI front end that would present that XML in a graphical form and allow you as much facility and complexity as want to write.
In the end, it is the XML file that is the authority on the interface structure, and which can be converted very easily with Perl to any format that you desire.
Also, following on from what #MatsPetersson said in his comment, I would much rather see
{9, 'b', 'l', 'a', 'b', 'l', 'a', 'b', 'l', 'a'}, // 1: blablabla
which is certain to be acceptable to as a uint8_t [] initialiser.
If you want further help then you will have to expand your description with a line-by-line explanation of what your "desired output" means in English.

how to prove that one certificate is issuer of another certificates

I have two certificate. One certificate is issuer of another certificate.
how can I see with java code that, my issuer certificate is really issuer?
I know that AuthorityKeyIdentifier of my certificate and SubjectKeyIdentifie of issuer certifiactes must be the same. I checked and they are the same.
but with java code I have that result:
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream usrCertificateIn = new FileInputStream("/usr.cer");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(usrCertificateIn);
InputStream SiningCACertificateIn = new FileInputStream("/siningCA.cer");
X509Certificate issuer = (X509Certificate) certFactory.generateCertificate(SiningCACertificateIn);
byte[] octets = (ASN1OctetString.getInstance(cert.getExtensionValue("2.5.29.35")).getOctets());
System.out.println(Arrays.toString(octets) + " bouncycastle, AuthorityKeyIdentifier");
System.out.println(Arrays.toString(cert.getExtensionValue("2.5.29.35")) + "java.security, AuthorityKeyIdentifier");
octets = ASN1OctetString.getInstance(issuer.getExtensionValue("2.5.29.14")).getOctets();
System.out.println((Arrays.toString(octets) + "bouncycastle, SubjectKeyIdentifie "));
System.out.println(Arrays.toString(issuer.getExtensionValue("2.5.29.14")) + "java.security, SubjectKeyIdentifie ");
adn the result is that:
[48, 22, -128, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113] bouncycastle, AuthorityKeyIdentifier
[4, 24, 48, 22, -128, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113]java.security, AuthorityKeyIdentifier
and another byte array that MUST BE THE SAME, BUT IT IS NOT another bytes is added at the beginning of the array.
[4, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113]bouncycastle, SubjectKeyIdentifie
[4, 22, 4, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113]java.security, SubjectKeyIdentifie
question 1)
Can I calculates the key Identifiers to get the same Arrays?
question 2)
is there another way in order to prove that one certificate is issuer of another certificates.
AuthorityKeyIdentifier and SubjectKeyIdentifier are defined differently:
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL,
authorityCertIssuer [1] GeneralNames OPTIONAL,
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
SubjectKeyIdentifier ::= KeyIdentifier
KeyIdentifier ::= OCTET STRING
(sections 4.2.1.1 and 4.2.1.2 of RFC 5280)
Thus, merely comparing the extension values cannot work, instead you have to extract the KeyIdentifier contents and compare them, e.g. using BouncyCastle ASN.1 helper classes.
BTW, the actual key identifier bytes are only
52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113
The 4, 20 before that indicates an OCTET STRING, 20 bytes long. In the AuthorityKeyIdentifier the 4 is replaced by the tag [0] (byte -128) due to implicit tagging.
The 48, 22 before that in your AuthorityKeyIdentifier means (constructed) SEQUENCE, 22 bytes long.
Etc. etc.
Thus,
Can I calculates the key Identifiers to get the same Arrays?
Yes, drill down to the actual KeyIdentifier OCTET STRING values.
is there another way in order to prove that one certificate is issuer of another certificates
Well, you can check whether the signature in the certificate is signed by the private key associated with the assumed issuer certificate by verifying against that certificate's public key.
PS: Concerning the question in the comments
is key identifier's length always 20? is it fixed? may be no, is not it?
No, it is not. The before mentioned RFC 5280 says:
For CA certificates, subject key identifiers SHOULD be derived from
the public key or a method that generates unique values. Two common
methods for generating key identifiers from the public key are:
(1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
value of the BIT STRING subjectPublicKey (excluding the tag,
length, and number of unused bits).
(2) The keyIdentifier is composed of a four-bit type field with
the value 0100 followed by the least significant 60 bits of
the SHA-1 hash of the value of the BIT STRING
subjectPublicKey (excluding the tag, length, and number of
unused bits).
Other methods of generating unique numbers are also acceptable.
I assume your CA uses method 1 (160 bit = 20 bytes) but this is merely a common method, not even something explicitly recommended, let alone required. Thus, no, you cannot count on the identifier being 20 bytes long.
PPS: Concerning the question in the comments
Isn't the signature the only way to truly prove one cert is issued by another?
That is no prove of the issuer-issuedBy relation either, it merely proves (at least to a certain degree) that the private key associated with the assumed issuer certificate signed the inspected certificate but there are situations where the same private-public key pair is used in multiple certificates.
In essence you need to do multiple complementary test and even then have to trust the CA not to do weird stuff. Not long ago e.g. the Swisscom changed one of their CA certificates to include an additional extension or critical attribute (I'd have to look up the details; I think someone certifying them demanded that change), and by the certificate signature verification test old signer certificates now seem to be issued by the new CA certificate even though the owners of the signer certificates might not be aware of that new extension/critical attribute.
So eventually real life simply is not as simple as one would like it to be...
To prove one certificate was issued by another, you should prove it was signed by the private key corresponding to the public key in the issuing certificate.
Let's call 2 certificates caCert and issuedCert. These are of type X509Certificate.
The Java code to prove issuedCert is signed by the entity represented by caCert is quite simple.
PublicKey caPubKey = caCert.getPublicKey();
issuedCert.verify(caPubKey);
If the verify method returns without throwing an exception, then issuedCert was signed by the private key corresponding to the public key in caCert.