如何为单元测试生成 Android Key Attestation 格式的证书?
How to generate certificate in Android Key Attestation format for unit tests?
我需要在服务器上验证由 Android 密钥证明过程生成的证书,但是我还没有访问真实设备的权限。
如何生成扩展字段与真实设备相同的 X.509 证书?
显然,根证书将是自签名的。
https://github.com/google/android-key-attestation/tree/master/server/examples中有示例如何使用我的值生成相同的证书?
我更喜欢服务器端 Java 和 Go。
除了使用真实设备之外,没有其他方法可以生成真实的 Google 签名证书。
出于测试目的,可以生成 Android 证书,其值与 Android 密钥证明过程生成的值相同,但是使用其他根证书签名。
预期的证书结构
- 使用密钥证明验证硬件支持的密钥对
- Key and ID Attestation: Certificate extension data schema
- Android Key Attestation Sample
代码如下:
(1) 从文件 google-root-ca/google-1.pem
中读取 Google 根证书并将其作为模板创建一个具有相同内容的新证书。
(2) 使用步骤 (1) 中生成的根证书签署的硬件证明 ID 序列创建 Android 证书
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"io/ioutil"
"math/big"
"time"
)
func main() {
rootCACertBytes, rootCAPrivKey, err := createCARoot()
if err != nil {
panic(err)
}
rootCACert, err := x509.ParseCertificate(rootCACertBytes)
if err != nil {
panic(err)
}
androidCertBytes, err := createAndroidKeystoreKey(rootCACert, rootCAPrivKey)
if err != nil {
panic(err)
}
err = printCertInPEM(rootCACertBytes, "CA.pem")
if err != nil {
panic(err)
}
err = printCertInPEM(androidCertBytes, "android.pem")
if err != nil {
panic(err)
}
}
func createCARoot() ([]byte, *rsa.PrivateKey, error) {
privKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
certTemplate, err := readGoogleRootCA()
if err != nil {
return nil, nil, err
}
cert, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, privKey.Public(), privKey)
if err != nil {
return nil, nil, err
}
return cert, privKey, nil
}
func randomSerialNumber() (*big.Int, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
return rand.Int(rand.Reader, serialNumberLimit)
}
func readGoogleRootCA() (*x509.Certificate, error) {
pemBytes, err := ioutil.ReadFile("google-root-ca/google-1.pem")
if err != nil {
return nil, err
}
decoded, rest := pem.Decode(pemBytes)
_ = rest
return x509.ParseCertificate(decoded.Bytes)
}
func printCertInPEM(certBytes []byte, outFile string) error {
return ioutil.WriteFile(outFile, pem.EncodeToMemory(
&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}), 0644)
}
var androidKeyAttestationOID = []int{1, 3, 6, 1, 4, 1, 11129, 2, 1, 17}
func createAndroidKeystoreKey(rootCACert *x509.Certificate, rootCAKey *rsa.PrivateKey) ([]byte, error) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
expiration := time.Now().AddDate(10, 0, 0)
serialNumber, err := randomSerialNumber()
if err != nil {
return nil, err
}
androidKeyAttestationExtension, err := CreateAndroidKeyAttestationExtension()
if err != nil {
return nil, err
}
certTemplate := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "Android Keystore Key",
},
NotBefore: time.Now(),
NotAfter: expiration,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtraExtensions: []pkix.Extension{
{
Id: androidKeyAttestationOID,
Value: androidKeyAttestationExtension,
},
},
}
return x509.CreateCertificate(rand.Reader, certTemplate, rootCACert, privKey.Public(), rootCAKey)
}
func CreateAndroidKeyAttestationExtension() ([]byte, error) {
keyDesc := KeyDescription{
AttestationVersion: 0,
AttestationSecurityLevel: 1,
KeymasterVersion: 0,
KeymasterSecurityLevel: 0,
AttestationChallenge: []byte("abcdefg"),
UniqueID: []byte{},
SoftwareEnforced: AuthorizationList{
AttestationIDSerial: []byte{},
},
TeeEnforced: AuthorizationList{
AttestationIDSerial: []byte("00112233445566778899"),
},
}
return asn1.Marshal(keyDesc)
}
type KeyDescription struct {
AttestationVersion int
AttestationSecurityLevel asn1.Enumerated
KeymasterVersion int
KeymasterSecurityLevel asn1.Enumerated
AttestationChallenge []byte
UniqueID []byte
SoftwareEnforced AuthorizationList
TeeEnforced AuthorizationList
}
// Values are commented out as nil values fail
type AuthorizationList struct {
//Purpose []int `asn1:"tag:1,explicit,set,optional"`
//Algorithm int `asn1:"tag:2,explicit,optional"`
//KeySize int `asn1:"tag:3,explicit,optional"`
//Digest []int `asn1:"tag:5,explicit,set,optional"`
//Padding []int `asn1:"tag:6,explicit,set,optional"`
//EcCurve int `asn1:"tag:10,explicit,optional"`
//RsaPublicExponent int `asn1:"tag:200,explicit,optional"`
//RollbackResistance interface{} `asn1:"tag:303,explicit,optional"`
//ActiveDateTime int `asn1:"tag:400,explicit,optional"`
//OriginationExpireDateTime int `asn1:"tag:401,explicit,optional"`
//UsageExpireDateTime int `asn1:"tag:402,explicit,optional"`
//NoAuthRequired interface{} `asn1:"tag:503,explicit,optional"`
//UserAuthType int `asn1:"tag:504,explicit,optional"`
//AuthTimeout int `asn1:"tag:505,explicit,optional"`
//AllowWhileOnBody interface{} `asn1:"tag:506,explicit,optional"`
//TrustedUserPresenceRequired interface{} `asn1:"tag:507,explicit,optional"`
//TrustedConfirmationRequired interface{} `asn1:"tag:508,explicit,optional"`
//UnlockedDeviceRequired interface{} `asn1:"tag:509,explicit,optional"`
//AllApplications interface{} `asn1:"tag:600,explicit,optional"`
//ApplicationID interface{} `asn1:"tag:601,explicit,optional"`
//CreationDateTime int `asn1:"tag:701,explicit,optional"`
//Origin int `asn1:"tag:702,explicit,optional"`
//RootOfTrust RootOfTrust `asn1:"tag:704,explicit,optional"`
//OsVersion int `asn1:"tag:705,explicit,optional"`
//OsPatchLevel int `asn1:"tag:706,explicit,optional"`
//AttestationApplicationID []byte `asn1:"tag:709,explicit,optional"`
//AttestationIDBrand []byte `asn1:"tag:710,explicit,optional"`
//AttestationIDDevice []byte `asn1:"tag:711,explicit,optional"`
//AttestationIDProduct []byte `asn1:"tag:712,explicit,optional"`
AttestationIDSerial []byte `asn1:"tag:713,explicit,optional"`
//AttestationIDImei []byte `asn1:"tag:714,explicit,optional"`
//AttestationIDMeid []byte `asn1:"tag:715,explicit,optional"`
//AttestationIDManufacturer []byte `asn1:"tag:716,explicit,optional"`
//AttestationIDModel []byte `asn1:"tag:717,explicit,optional"`
//VendorPatchLevel int `asn1:"tag:718,explicit,optional"`
//BootPatchLevel int `asn1:"tag:719,explicit,optional"`
}
type RootOfTrust struct {
VerifiedBootKey []byte
DeviceLocked bool
VerifiedBootState VerifiedBootState
VerifiedBootHash []byte
}
type VerifiedBootState int
const (
Verified VerifiedBootState = iota
SelfSigned
Unverified
Failed
)
google-root-ca/google-1.pem
将second Google root certificate的内容保存到上面代码中使用的google-root-ca/google-1.pem
中。
我需要在服务器上验证由 Android 密钥证明过程生成的证书,但是我还没有访问真实设备的权限。
如何生成扩展字段与真实设备相同的 X.509 证书?
显然,根证书将是自签名的。
https://github.com/google/android-key-attestation/tree/master/server/examples中有示例如何使用我的值生成相同的证书?
我更喜欢服务器端 Java 和 Go。
除了使用真实设备之外,没有其他方法可以生成真实的 Google 签名证书。
出于测试目的,可以生成 Android 证书,其值与 Android 密钥证明过程生成的值相同,但是使用其他根证书签名。
预期的证书结构
- 使用密钥证明验证硬件支持的密钥对
- Key and ID Attestation: Certificate extension data schema
- Android Key Attestation Sample
代码如下:
(1) 从文件 google-root-ca/google-1.pem
中读取 Google 根证书并将其作为模板创建一个具有相同内容的新证书。
(2) 使用步骤 (1) 中生成的根证书签署的硬件证明 ID 序列创建 Android 证书
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"io/ioutil"
"math/big"
"time"
)
func main() {
rootCACertBytes, rootCAPrivKey, err := createCARoot()
if err != nil {
panic(err)
}
rootCACert, err := x509.ParseCertificate(rootCACertBytes)
if err != nil {
panic(err)
}
androidCertBytes, err := createAndroidKeystoreKey(rootCACert, rootCAPrivKey)
if err != nil {
panic(err)
}
err = printCertInPEM(rootCACertBytes, "CA.pem")
if err != nil {
panic(err)
}
err = printCertInPEM(androidCertBytes, "android.pem")
if err != nil {
panic(err)
}
}
func createCARoot() ([]byte, *rsa.PrivateKey, error) {
privKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
certTemplate, err := readGoogleRootCA()
if err != nil {
return nil, nil, err
}
cert, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, privKey.Public(), privKey)
if err != nil {
return nil, nil, err
}
return cert, privKey, nil
}
func randomSerialNumber() (*big.Int, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
return rand.Int(rand.Reader, serialNumberLimit)
}
func readGoogleRootCA() (*x509.Certificate, error) {
pemBytes, err := ioutil.ReadFile("google-root-ca/google-1.pem")
if err != nil {
return nil, err
}
decoded, rest := pem.Decode(pemBytes)
_ = rest
return x509.ParseCertificate(decoded.Bytes)
}
func printCertInPEM(certBytes []byte, outFile string) error {
return ioutil.WriteFile(outFile, pem.EncodeToMemory(
&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}), 0644)
}
var androidKeyAttestationOID = []int{1, 3, 6, 1, 4, 1, 11129, 2, 1, 17}
func createAndroidKeystoreKey(rootCACert *x509.Certificate, rootCAKey *rsa.PrivateKey) ([]byte, error) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
expiration := time.Now().AddDate(10, 0, 0)
serialNumber, err := randomSerialNumber()
if err != nil {
return nil, err
}
androidKeyAttestationExtension, err := CreateAndroidKeyAttestationExtension()
if err != nil {
return nil, err
}
certTemplate := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "Android Keystore Key",
},
NotBefore: time.Now(),
NotAfter: expiration,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtraExtensions: []pkix.Extension{
{
Id: androidKeyAttestationOID,
Value: androidKeyAttestationExtension,
},
},
}
return x509.CreateCertificate(rand.Reader, certTemplate, rootCACert, privKey.Public(), rootCAKey)
}
func CreateAndroidKeyAttestationExtension() ([]byte, error) {
keyDesc := KeyDescription{
AttestationVersion: 0,
AttestationSecurityLevel: 1,
KeymasterVersion: 0,
KeymasterSecurityLevel: 0,
AttestationChallenge: []byte("abcdefg"),
UniqueID: []byte{},
SoftwareEnforced: AuthorizationList{
AttestationIDSerial: []byte{},
},
TeeEnforced: AuthorizationList{
AttestationIDSerial: []byte("00112233445566778899"),
},
}
return asn1.Marshal(keyDesc)
}
type KeyDescription struct {
AttestationVersion int
AttestationSecurityLevel asn1.Enumerated
KeymasterVersion int
KeymasterSecurityLevel asn1.Enumerated
AttestationChallenge []byte
UniqueID []byte
SoftwareEnforced AuthorizationList
TeeEnforced AuthorizationList
}
// Values are commented out as nil values fail
type AuthorizationList struct {
//Purpose []int `asn1:"tag:1,explicit,set,optional"`
//Algorithm int `asn1:"tag:2,explicit,optional"`
//KeySize int `asn1:"tag:3,explicit,optional"`
//Digest []int `asn1:"tag:5,explicit,set,optional"`
//Padding []int `asn1:"tag:6,explicit,set,optional"`
//EcCurve int `asn1:"tag:10,explicit,optional"`
//RsaPublicExponent int `asn1:"tag:200,explicit,optional"`
//RollbackResistance interface{} `asn1:"tag:303,explicit,optional"`
//ActiveDateTime int `asn1:"tag:400,explicit,optional"`
//OriginationExpireDateTime int `asn1:"tag:401,explicit,optional"`
//UsageExpireDateTime int `asn1:"tag:402,explicit,optional"`
//NoAuthRequired interface{} `asn1:"tag:503,explicit,optional"`
//UserAuthType int `asn1:"tag:504,explicit,optional"`
//AuthTimeout int `asn1:"tag:505,explicit,optional"`
//AllowWhileOnBody interface{} `asn1:"tag:506,explicit,optional"`
//TrustedUserPresenceRequired interface{} `asn1:"tag:507,explicit,optional"`
//TrustedConfirmationRequired interface{} `asn1:"tag:508,explicit,optional"`
//UnlockedDeviceRequired interface{} `asn1:"tag:509,explicit,optional"`
//AllApplications interface{} `asn1:"tag:600,explicit,optional"`
//ApplicationID interface{} `asn1:"tag:601,explicit,optional"`
//CreationDateTime int `asn1:"tag:701,explicit,optional"`
//Origin int `asn1:"tag:702,explicit,optional"`
//RootOfTrust RootOfTrust `asn1:"tag:704,explicit,optional"`
//OsVersion int `asn1:"tag:705,explicit,optional"`
//OsPatchLevel int `asn1:"tag:706,explicit,optional"`
//AttestationApplicationID []byte `asn1:"tag:709,explicit,optional"`
//AttestationIDBrand []byte `asn1:"tag:710,explicit,optional"`
//AttestationIDDevice []byte `asn1:"tag:711,explicit,optional"`
//AttestationIDProduct []byte `asn1:"tag:712,explicit,optional"`
AttestationIDSerial []byte `asn1:"tag:713,explicit,optional"`
//AttestationIDImei []byte `asn1:"tag:714,explicit,optional"`
//AttestationIDMeid []byte `asn1:"tag:715,explicit,optional"`
//AttestationIDManufacturer []byte `asn1:"tag:716,explicit,optional"`
//AttestationIDModel []byte `asn1:"tag:717,explicit,optional"`
//VendorPatchLevel int `asn1:"tag:718,explicit,optional"`
//BootPatchLevel int `asn1:"tag:719,explicit,optional"`
}
type RootOfTrust struct {
VerifiedBootKey []byte
DeviceLocked bool
VerifiedBootState VerifiedBootState
VerifiedBootHash []byte
}
type VerifiedBootState int
const (
Verified VerifiedBootState = iota
SelfSigned
Unverified
Failed
)
google-root-ca/google-1.pem
将second Google root certificate的内容保存到上面代码中使用的google-root-ca/google-1.pem
中。