blob: 60fee4a51ab0cd1240abba5f49bb698f95f84116 [file] [log] [blame]
/**
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.remoteprovisioner;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.util.ArrayList;
/**
* Provides convenience methods for parsing certificates and extracting information.
*/
public class X509Utils {
/**
* Takes a byte array composed of DER encoded certificates and returns the X.509 certificates
* contained within as an X509Certificate array.
*/
public static X509Certificate[] formatX509Certs(byte[] certStream)
throws CertificateException {
CertificateFactory fact = CertificateFactory.getInstance("X.509");
ByteArrayInputStream in = new ByteArrayInputStream(certStream);
ArrayList<Certificate> certs = new ArrayList<Certificate>(fact.generateCertificates(in));
return certs.toArray(new X509Certificate[certs.size()]);
}
/**
* Extracts an ECDSA-P256 key from a certificate and formats it so that it can be used to match
* the certificate chain to the proper key when passed into the keystore database.
*/
public static byte[] getAndFormatRawPublicKey(X509Certificate cert) {
ECPublicKey key = (ECPublicKey) cert.getPublicKey();
// Remote key provisioning internally supports the default, uncompressed public key
// format for ECDSA. This defines the format as (s | x | y), where s is the byte
// indicating if the key is compressed or not, and x and y make up the EC point.
// However, the key as stored in a COSE_Key object is just the two points. As such,
// the raw public key is stored in the database as (x | y), so the compression byte
// should be dropped here. Leading 0's must be preserved.
//
// s: 1 byte, x: 32 bytes, y: 32 bytes
BigInteger xCoord = key.getW().getAffineX();
BigInteger yCoord = key.getW().getAffineY();
byte[] formattedKey = new byte[64];
byte[] xBytes = xCoord.toByteArray();
// BigInteger returns the value as two's complement big endian byte encoding. This means
// that a positive, 32-byte value with a leading 1 bit will be converted to a byte array of
// length 33 in order to include a leading 0 bit.
if (xBytes.length == 33) {
System.arraycopy(xBytes, 1 /* offset */, formattedKey, 0 /* offset */, 32);
} else {
System.arraycopy(xBytes, 0 /* offset */,
formattedKey, 32 - xBytes.length, xBytes.length);
}
byte[] yBytes = yCoord.toByteArray();
if (yBytes.length == 33) {
System.arraycopy(yBytes, 1 /* offset */, formattedKey, 32 /* offset */, 32);
} else {
System.arraycopy(yBytes, 0 /* offset */,
formattedKey, 64 - yBytes.length, yBytes.length);
}
return formattedKey;
}
}