Как сгенерировать сертификат в формате аттестации ключей Android для модульных тестов?

#android #x509

#Android #x509

Вопрос:

Мне нужно проверить сертификаты, сгенерированные процессом аттестации ключей Android на сервере, однако у меня пока нет доступа к реальному устройству.

Как я могу сгенерировать сертификат X.509 с полями расширения, аналогичными тем, которые я должен получить с реального устройства?

Очевидно, что корневой сертификат будет самоподписанным.

Есть примеры в https://github.com/google/android-key-attestation/tree/master/server/examples Как сгенерировать те же сертификаты с моими значениями?

Я предпочитаю Java и Go на стороне сервера.

Ответ №1:

Нет другого способа сгенерировать настоящие сертификаты, подписанные Google, кроме использования реального устройства.

В целях тестирования можно сгенерировать сертификаты Android с теми же значениями, что и в процессе аттестации ключей Android, однако подписанные другим корневым сертификатом.

Ожидаемая структура сертификата

Приведенный ниже код: (1) Считывает корневой сертификат Google из файла google-root-ca/google-1.pem и использует его в качестве шаблона для создания нового сертификата с тем же содержимым. (2) Создает сертификат Android с аппаратно подтвержденным идентификатором, подписанным корневым сертификатом, сгенерированным на шаге (1)

 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(
        amp;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 := amp;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

Сохраните содержимое второго корневого сертификата Google в google-root-ca/google-1.pem used в приведенном выше коде.