Skip to content

Commit

Permalink
Android keychain handler (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbSteveK committed Feb 8, 2024
1 parent 1ff5840 commit 8d1c5c9
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 142 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
package software.amazon.awssdk.crt.io;

import java.security.PrivateKey;
import java.security.Signature;
import java.io.ByteArrayOutputStream;

/*
* TlsKeyOperationHandler implementation for using an Android PrivateKey on TlsKeyOperations
*/
public class TlsAndroidPrivateKeyOperationHandler implements TlsKeyOperationHandler {
private PrivateKey privateKey;

/*
* DER encoded DigestInfo value to be prefixed to the hash, used for RSA signing
* See https://tools.ietf.org/html/rfc3447#page-43
*/
static byte[] sha1PrefixToRsaSig = { (byte)0x30, (byte)0x21, (byte)0x30, (byte)0x09, (byte)0x06, (byte)0x05, (byte)0x2b, (byte)0x0e, (byte)0x03, (byte)0x02, (byte)0x1a, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x14 };
static byte[] sha224PrefixToRsaSig = { (byte)0x30, (byte)0x2d, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x04, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x1c };
static byte[] sha256PrefixToRsaSig = { (byte)0x30, (byte)0x31, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x20 };
static byte[] sha384PrefixToRsaSig = { (byte)0x30, (byte)0x41, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x02, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x30 };
static byte[] sha512PrefixToRsaSig = { (byte)0x30, (byte)0x51, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02, (byte)0x03, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x40 };

public TlsAndroidPrivateKeyOperationHandler(PrivateKey privateKey) {
this.privateKey = privateKey;
}

public void performOperation(TlsKeyOperation operation){
try{
if (operation.getType() != TlsKeyOperation.Type.SIGN) {
operation.completeExceptionally(new Throwable("Android KeyChain PrivateKey only handles SIGN operations."));
return;
}

// A SIGN operation's inputData is the 32bytes of the SHA-256 digest.
// Before doing the RSA signature, we need to construct a PKCS1 v1.5 DigestInfo.
// See https://datatracker.ietf.org/doc/html/rfc3447#section-9.2
byte[] dataToSign = operation.getInput();

Signature signature = Signature.getInstance("NONEwith" + operation.getSignatureAlgorithm().name());;

switch(operation.getSignatureAlgorithm()){
case RSA:
/*
* DER encoded DigestInfo value to be prefixed to the hash, used for RSA signing
* See https://tools.ietf.org/html/rfc3447#page-43
*/
byte[] digestAlgorithm;
switch(operation.getDigestAlgorithm()){
case SHA1:
digestAlgorithm = sha1PrefixToRsaSig;
break;
case SHA224:
digestAlgorithm = sha224PrefixToRsaSig;
break;
case SHA256:
digestAlgorithm = sha256PrefixToRsaSig;
break;
case SHA384:
digestAlgorithm = sha384PrefixToRsaSig;
break;
case SHA512:
digestAlgorithm = sha512PrefixToRsaSig;
break;
case UNKNOWN:
default:
operation.completeExceptionally(new Throwable("An UNKNOWN digest algorithm was encountered during a SIGN operation against an Android KeyChain PrivateKey."));
return;
}

ByteArrayOutputStream digestInfoStream = new ByteArrayOutputStream();
digestInfoStream.write(digestAlgorithm);
digestInfoStream.write(dataToSign);
byte[] digestInfo = digestInfoStream.toByteArray();

signature.initSign(privateKey);
signature.update(digestInfo);
byte[] signatureBytesRSA = signature.sign();

operation.complete(signatureBytesRSA);
return;

case ECDSA:

signature.initSign(privateKey);
signature.update(dataToSign);
byte[] signatureBytesECC = signature.sign();

operation.complete(signatureBytesECC);
return;

case UNKNOWN:
default:

operation.completeExceptionally(new Throwable("An UNKNOWN signature algorithm was encountered during a SIGN operation against an Android KeyChain PrivateKey."));
return;
}
} catch (Exception ex){
operation.completeExceptionally(new Throwable("Exception caught during Android KeyChain PrivateKey operation.", ex));
}
}
}
3 changes: 2 additions & 1 deletion src/main/java/software/amazon/awssdk/crt/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ public enum LogSubject {
// aws-crt-java, we're authoritative
JavaCrtGeneral(0x2400),
JavaCrtResource(0x2401),
JavaCrtS3(0x2402)
JavaCrtS3(0x2402),
JavaAndroidKeychain(0x2403)
;

LogSubject(int value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public TlsContextCustomKeyOperationOptions withCertificateFileContents(String co
}

/**
* Returns the path to the X.509 certificate file on desk if it has been set.
* Returns the path to the X.509 certificate file on disk if it has been set.
*
* @return The path to the certificate file
*/
Expand Down
2 changes: 2 additions & 0 deletions src/native/crt.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ static struct aws_log_subject_info s_crt_log_subject_infos[] = {
AWS_LS_JAVA_CRT_RESOURCE, "JavaCrtResource", "Subject for CrtResource"),
DEFINE_LOG_SUBJECT_INFO(
AWS_LS_JAVA_CRT_S3, "JavaCrtS3", "Subject for the layer binding aws-c-s3 to Java"),
DEFINE_LOG_SUBJECT_INFO(
AWS_LS_JAVA_ANDROID_KEYCHAIN, "android-keychain", "Subject for Android KeyChain"),
/* clang-format on */
};

Expand Down
1 change: 1 addition & 0 deletions src/native/crt.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum aws_java_crt_log_subject {
AWS_LS_JAVA_CRT_GENERAL = AWS_LOG_SUBJECT_BEGIN_RANGE(AWS_CRT_JAVA_PACKAGE_ID),
AWS_LS_JAVA_CRT_RESOURCE,
AWS_LS_JAVA_CRT_S3,
AWS_LS_JAVA_ANDROID_KEYCHAIN,

AWS_LS_JAVA_CRT_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_CRT_JAVA_PACKAGE_ID),
};
Expand Down

0 comments on commit 8d1c5c9

Please sign in to comment.